#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include <X11/Xfuncs.h>
#include <X11/Xaw/StripCharP.h>
#include <X11/Xaw/XawInit.h>
#include "Private.h"
#define MS_PER_SEC 1000
static void XawStripChartInitialize(Widget, Widget, ArgList, Cardinal*);
static void XawStripChartDestroy(Widget);
static void XawStripChartRedisplay(Widget, XEvent*, Region);
static void XawStripChartResize(Widget);
static Boolean XawStripChartSetValues(Widget, Widget, Widget,
ArgList, Cardinal*);
static void CreateGC(StripChartWidget, unsigned int);
static void DestroyGC(StripChartWidget, unsigned int);
static void draw_it(XtPointer, XtIntervalId*);
static void MoveChart(StripChartWidget, Bool);
static int repaint_window(StripChartWidget, int, int);
#define offset(field) XtOffsetOf(StripChartRec, field)
static XtResource resources[] = {
{
XtNwidth,
XtCWidth,
XtRDimension,
sizeof(Dimension),
offset(core.width),
XtRImmediate,
(XtPointer)
120
},
{
XtNheight,
XtCHeight,
XtRDimension,
sizeof(Dimension),
offset(core.height),
XtRImmediate,
(XtPointer)120
},
{
XtNupdate,
XtCInterval,
XtRInt,
sizeof(int),
offset(strip_chart.update),
XtRImmediate,
(XtPointer)10
},
{
XtNminScale,
XtCScale,
XtRInt,
sizeof(int),
offset(strip_chart.min_scale),
XtRImmediate,
(XtPointer)1
},
{
XtNforeground,
XtCForeground,
XtRPixel,
sizeof(Pixel),
offset(strip_chart.fgpixel),
XtRString,
XtDefaultForeground
},
{
XtNhighlight,
XtCForeground,
XtRPixel,
sizeof(Pixel),
offset(strip_chart.hipixel),
XtRString,
XtDefaultForeground
},
{
XtNgetValue,
XtCCallback,
XtRCallback,
sizeof(XtPointer),
offset(strip_chart.get_value),
XtRImmediate,
NULL
},
{
XtNjumpScroll,
XtCJumpScroll,
XtRInt,
sizeof(int),
offset(strip_chart.jump_val),
XtRImmediate,
(XtPointer)DEFAULT_JUMP
},
};
#undef offset
StripChartClassRec stripChartClassRec = {
{
(WidgetClass)&simpleClassRec,
"StripChart",
sizeof(StripChartRec),
XawInitializeWidgetSet,
NULL,
False,
XawStripChartInitialize,
NULL,
XtInheritRealize,
NULL,
0,
resources,
XtNumber(resources),
NULLQUARK,
True,
XtExposeCompressMultiple
| XtExposeGraphicsExposeMerged,
True,
False,
XawStripChartDestroy,
XawStripChartResize,
XawStripChartRedisplay,
XawStripChartSetValues,
NULL,
NULL,
NULL,
NULL,
XtVersion,
NULL,
NULL,
XtInheritQueryGeometry,
XtInheritDisplayAccelerator,
NULL,
},
{
XtInheritChangeSensitive,
}
};
WidgetClass stripChartWidgetClass = (WidgetClass)&stripChartClassRec;
static void
CreateGC(StripChartWidget w, unsigned int which)
{
XGCValues myXGCV;
if (which & FOREGROUND) {
myXGCV.foreground = w->strip_chart.fgpixel;
w->strip_chart.fgGC = XtGetGC((Widget)w, GCForeground, &myXGCV);
}
if (which & HIGHLIGHT) {
myXGCV.foreground = w->strip_chart.hipixel;
w->strip_chart.hiGC = XtGetGC((Widget)w, GCForeground, &myXGCV);
}
}
static void
DestroyGC(StripChartWidget w, unsigned int which)
{
if (which & FOREGROUND)
XtReleaseGC((Widget)w, w->strip_chart.fgGC);
if (which & HIGHLIGHT)
XtReleaseGC((Widget)w, w->strip_chart.hiGC);
}
static void
XawStripChartInitialize(Widget greq, Widget gnew,
ArgList args, Cardinal *num_args)
{
StripChartWidget w = (StripChartWidget)gnew;
if (w->strip_chart.update > 0)
w->strip_chart.interval_id =
XtAppAddTimeOut(XtWidgetToApplicationContext(gnew),
w->strip_chart.update * MS_PER_SEC,
draw_it, (XtPointer)gnew);
CreateGC(w, ALL_GCS);
w->strip_chart.scale = w->strip_chart.min_scale;
w->strip_chart.interval = 0;
w->strip_chart.max_value = 0.0;
w->strip_chart.points = NULL;
XawStripChartResize(gnew);
}
static void
XawStripChartDestroy(Widget gw)
{
StripChartWidget w = (StripChartWidget)gw;
if (w->strip_chart.update > 0)
XtRemoveTimeOut(w->strip_chart.interval_id);
if (w->strip_chart.points)
XtFree((char *)w->strip_chart.points);
DestroyGC(w, ALL_GCS);
}
static void
XawStripChartRedisplay(Widget w, XEvent *event, Region region)
{
if (event->type == GraphicsExpose)
(void)repaint_window((StripChartWidget)w, event->xgraphicsexpose.x,
event->xgraphicsexpose.width);
else
(void)repaint_window((StripChartWidget)w, event->xexpose.x,
event->xexpose.width);
}
static void
draw_it(XtPointer client_data, XtIntervalId *id)
{
StripChartWidget w = (StripChartWidget)client_data;
double value;
if (w->strip_chart.update > 0)
w->strip_chart.interval_id =
XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)w),
w->strip_chart.update * MS_PER_SEC,draw_it,
client_data);
if (w->strip_chart.interval >= XtWidth(w))
MoveChart((StripChartWidget)w, True);
if (w->strip_chart.get_value == NULL)
return;
XtCallCallbacks((Widget)w, XtNgetValue, (XtPointer)&value);
if (value > w->strip_chart.max_value) {
w->strip_chart.max_value = value;
if (XtIsRealized((Widget)w) &&
w->strip_chart.max_value > w->strip_chart.scale) {
XClearWindow(XtDisplay(w), XtWindow(w));
w->strip_chart.interval = repaint_window(w, 0, XtWidth(w));
}
}
w->strip_chart.valuedata[w->strip_chart.interval] = value;
if (XtIsRealized((Widget)w)) {
int y = (int)(XtHeight(w) - XtHeight(w) * value
/ w->strip_chart.scale);
XFillRectangle(XtDisplay(w), XtWindow(w), w->strip_chart.fgGC,
w->strip_chart.interval, y,
1, XtHeight(w) - y);
if (w->strip_chart.points != NULL) {
w->strip_chart.points[0].x = w->strip_chart.interval;
XDrawPoints(XtDisplay(w), XtWindow(w), w->strip_chart.hiGC,
w->strip_chart.points, w->strip_chart.scale - 1,
CoordModePrevious);
}
XFlush(XtDisplay(w));
}
w->strip_chart.interval++;
}
static int
repaint_window(StripChartWidget w, int left, int width)
{
int i, j;
int next = w->strip_chart.interval;
int scale = w->strip_chart.scale;
int scalewidth = 0;
if (w->strip_chart.interval != 0 || scale <= w->strip_chart.max_value)
scale = w->strip_chart.max_value + 1;
if (scale < w->strip_chart.min_scale)
scale = w->strip_chart.min_scale;
if (scale != w->strip_chart.scale) {
w->strip_chart.scale = scale;
left = 0;
width = next;
scalewidth = XtWidth(w);
XawStripChartResize((Widget)w);
if (XtIsRealized((Widget)w))
XClearWindow(XtDisplay(w), XtWindow(w));
}
if (XtIsRealized((Widget)w)) {
Display *dpy = XtDisplay(w);
Window win = XtWindow(w);
width += left - 1;
if (!scalewidth)
scalewidth = width;
if (next < ++width)
width = next;
for (i = left; i < width; i++) {
int y = XtHeight(w) - (XtHeight(w) * w->strip_chart.valuedata[i])
/ w->strip_chart.scale;
XFillRectangle(dpy, win, w->strip_chart.fgGC,
i, y, 1, XtHeight(w) - y);
}
for (i = 1; i < w->strip_chart.scale; i++) {
j = i * ((int)XtHeight(w) / w->strip_chart.scale);
XDrawLine(dpy, win, w->strip_chart.hiGC, left, j, scalewidth, j);
}
}
return (next);
}
static void
MoveChart(StripChartWidget w, Bool blit)
{
double old_max;
int left, i, j;
int next = w->strip_chart.interval;
if (!XtIsRealized((Widget)w))
return;
if (w->strip_chart.jump_val < 0)
w->strip_chart.jump_val = DEFAULT_JUMP;
if (w->strip_chart.jump_val == DEFAULT_JUMP)
j = XtWidth(w) >> 1;
else {
j = (int)XtWidth(w) - w->strip_chart.jump_val;
if (j < 0)
j = 0;
}
(void)memmove((char *)w->strip_chart.valuedata,
(char *)(w->strip_chart.valuedata + next - j),
j * sizeof(double));
next = w->strip_chart.interval = j;
old_max = w->strip_chart.max_value;
w->strip_chart.max_value = 0.0;
for (i = 0; i < next; i++) {
if (w->strip_chart.valuedata[i] > w->strip_chart.max_value)
w->strip_chart.max_value = w->strip_chart.valuedata[i];
}
if (!blit)
return;
if (old_max != w->strip_chart.max_value) {
XClearWindow(XtDisplay(w), XtWindow(w));
repaint_window(w, 0, XtWidth(w));
return;
}
XCopyArea(XtDisplay((Widget)w), XtWindow((Widget)w), XtWindow((Widget)w),
w->strip_chart.hiGC, (int)XtWidth(w) - j, 0, j, XtHeight(w), 0, 0);
XClearArea(XtDisplay((Widget)w), XtWindow((Widget)w),
j, 0, XtWidth(w) - j, XtHeight(w), False);
left = j;
for (i = 1; i < w->strip_chart.scale; i++) {
j = i * (XtHeight(w) / w->strip_chart.scale);
XDrawLine(XtDisplay((Widget)w), XtWindow((Widget)w),
w->strip_chart.hiGC, left, j, XtWidth(w), j);
}
}
static Boolean
XawStripChartSetValues(Widget current, Widget request, Widget cnew,
ArgList args, Cardinal *num_args)
{
StripChartWidget old = (StripChartWidget)current;
StripChartWidget w = (StripChartWidget)cnew;
Bool ret_val = False;
unsigned int new_gc = NO_GCS;
if (w->strip_chart.update != old->strip_chart.update) {
if (old->strip_chart.update > 0)
XtRemoveTimeOut(old->strip_chart.interval_id);
if (w->strip_chart.update > 0)
w->strip_chart.interval_id =
XtAppAddTimeOut(XtWidgetToApplicationContext(cnew),
w->strip_chart.update * MS_PER_SEC,
draw_it, (XtPointer)w);
}
if (w->strip_chart.min_scale > w->strip_chart.max_value + 1)
ret_val = True;
if (w->strip_chart.fgpixel != old->strip_chart.fgpixel) {
new_gc |= FOREGROUND;
ret_val = True;
}
if (w->strip_chart.hipixel != old->strip_chart.hipixel) {
new_gc |= HIGHLIGHT;
ret_val = True;
}
DestroyGC(old, new_gc);
CreateGC(w, new_gc);
return (ret_val);
}
static void
XawStripChartResize(Widget widget)
{
StripChartWidget w = (StripChartWidget)widget;
XPoint *points;
Cardinal size;
int i;
if (w->strip_chart.scale <= 1) {
XtFree((char *)w->strip_chart.points);
w->strip_chart.points = NULL;
return;
}
size = sizeof(XPoint) * (w->strip_chart.scale - 1);
points = (XPoint *)XtRealloc((XtPointer)w->strip_chart.points, size);
w->strip_chart.points = points;
for (i = 1; i < w->strip_chart.scale; i++) {
points[i - 1].x = 0;
points[i - 1].y = XtHeight(w) / w->strip_chart.scale;
}
}