mirror of https://github.com/XEphem/XEphem.git
3383 lines
95 KiB
C
3383 lines
95 KiB
C
/* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include <Xm/Xm.h>
|
|
#include <Xm/ArrowB.h>
|
|
#include <Xm/DrawingA.h>
|
|
#include <Xm/Form.h>
|
|
#include <Xm/Frame.h>
|
|
#include <Xm/Label.h>
|
|
#include <Xm/PushB.h>
|
|
#include <Xm/RowColumn.h>
|
|
#include <Xm/Separator.h>
|
|
#include <Xm/ScrolledW.h>
|
|
#include <Xm/PanedW.h>
|
|
#include <Xm/Scale.h>
|
|
#include <Xm/Text.h>
|
|
#include <Xm/TextF.h>
|
|
#include <Xm/ToggleB.h>
|
|
|
|
#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, "<getProperties version='%g'/>\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,
|
|
"<newBLOBVector device='%s' name='%s' timestamp='%s'>\n",
|
|
bvp->device, bvp->name, setTStampNow (tstamp));
|
|
started = 1;
|
|
}
|
|
fprintf (swfp, " <oneBLOB name='%s' size='%d' format='%s'>\n",
|
|
bp->name, filen, format);
|
|
for (i = 0; i < file64n; i += 72)
|
|
fprintf (swfp, " %.72s\n", file64+i);
|
|
fprintf (swfp, " </oneBLOB>\n");
|
|
|
|
free (file64);
|
|
XtFree (fn);
|
|
}
|
|
|
|
if (started) {
|
|
fprintf (swfp, "</newBLOBVector>\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,"<newNumberVector device='%s' name='%s' timestamp='%s'>\n",
|
|
nvp->device, nvp->name, setTStampNow (tstamp));
|
|
for (i = 0; i < nvp->nnp; i++) {
|
|
INumber *np = &nvp->np[i];
|
|
fprintf (swfp, " <oneNumber name='%s'>%.20g</oneNumber>\n",
|
|
np->name, np->value);
|
|
}
|
|
|
|
fprintf (swfp, "</newNumberVector>\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, "<newTextVector device='%s' name='%s' timestamp='%s'>\n",
|
|
tvp->device, tvp->name, setTStampNow (tstamp));
|
|
for (i = 0; i < tvp->ntp; i++) {
|
|
fprintf (swfp, " <oneText name='%s'>%s</oneText>\n",
|
|
tvp->tp[i].name, tvp->tp[i].text);
|
|
}
|
|
|
|
fprintf (swfp, "</newTextVector>\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,"<newSwitchVector device='%s' name='%s' timestamp='%s'>\n",
|
|
svp->device, svp->name, setTStampNow (tstamp));
|
|
|
|
for (i = 0; i < svp->nsp; i++) {
|
|
fprintf (swfp, " <oneSwitch name='%s'>%s</oneSwitch>\n", svp->sp[i].name,
|
|
svp->sp[i].s == ISS_ON ? "On" : "Off");
|
|
}
|
|
|
|
fprintf (swfp, "</newSwitchVector>\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, "<enableBLOB device='%s' name='%s'>%s</enableBLOB>\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, "<?xml version='1.0'?>\n");
|
|
}
|
|
|