X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/61ec970df30325dbcd8c9d0f0e431dc793126656..059ec3d9952740285fb1ebf47961b8aca2eb1b4a:/src/exim_monitor/em_StripChart.c diff --git a/src/exim_monitor/em_StripChart.c b/src/exim_monitor/em_StripChart.c new file mode 100644 index 000000000..316102147 --- /dev/null +++ b/src/exim_monitor/em_StripChart.c @@ -0,0 +1,507 @@ +/* $Cambridge: exim/src/exim_monitor/em_StripChart.c,v 1.1 2004/10/07 10:39:01 ph10 Exp $ */ +/* $XConsortium: StripChart.c,v 1.20 91/05/24 17:20:42 converse Exp $ */ + +/*********************************************************** +Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts, +and the Massachusetts Institute of Technology, Cambridge, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Digital or MIT not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ + +/* This is the Athena StripChart widget, slightly hacked by +Philip Hazel in order to give access to +its repaint_window function so that a repaint can be forced. + +The repaint_window function has also been nobbled so that it only +ever changes scale to 10. There is probably a better way to handle +this - such as inventing some new resources, but I'm not up to +that just at the moment. + +On SunOS4 there are name clashes when trying to link this with the +Athena library. So to avoid them, rename a few things by inserting +"my" at the front of "strip". */ + + +#include +#include +#include +#include +#include +#include + +#define MS_PER_SEC 1000 + +/* Private Data */ + +#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, (XtPointer) NULL}, + {XtNjumpScroll, XtCJumpScroll, XtRInt, sizeof(int), + offset(strip_chart.jump_val), XtRImmediate, (XtPointer) DEFAULT_JUMP}, +}; + +#undef offset + +/* Added argument types to these to shut picky compilers up. PH */ + +static void CreateGC(StripChartWidget, unsigned int); +static void DestroyGC(StripChartWidget, unsigned int); +static void Initialize(), Destroy(), Redisplay(); +static void MoveChart(StripChartWidget, Boolean); +static void SetPoints(StripChartWidget); +static Boolean SetValues(); + +int repaint_window(StripChartWidget, int, int); /* PH hack */ +/* static int repaint_window(); */ + +StripChartClassRec stripChartClassRec = { + { /* core fields */ + /* superclass */ (WidgetClass) &simpleClassRec, + /* class_name */ "StripChart", + /* size */ sizeof(StripChartRec), + /* class_initialize */ XawInitializeWidgetSet, + /* class_part_initialize */ NULL, + /* class_inited */ FALSE, + /* initialize */ Initialize, + /* initialize_hook */ NULL, + /* realize */ XtInheritRealize, + /* actions */ NULL, + /* num_actions */ 0, + /* resources */ resources, + /* num_resources */ XtNumber(resources), + /* xrm_class */ NULLQUARK, + /* compress_motion */ TRUE, + /* compress_exposure */ XtExposeCompressMultiple | + XtExposeGraphicsExposeMerged, + /* compress_enterleave */ TRUE, + /* visible_interest */ FALSE, + /* destroy */ Destroy, + /* resize */ (void (*)(Widget))SetPoints, + /* expose */ Redisplay, + /* set_values */ SetValues, + /* set_values_hook */ NULL, + /* set_values_almost */ NULL, + /* get_values_hook */ NULL, + /* accept_focus */ NULL, + /* version */ XtVersion, + /* callback_private */ NULL, + /* tm_table */ NULL, + /* query_geometry */ XtInheritQueryGeometry, + /* display_accelerator */ XtInheritDisplayAccelerator, + /* extension */ NULL + }, + { /* Simple class fields */ + /* change_sensitive */ XtInheritChangeSensitive + } +}; + +WidgetClass mystripChartWidgetClass = (WidgetClass) &stripChartClassRec; + +/**************************************************************** + * + * Private Procedures + * + ****************************************************************/ + +static void draw_it(); + +/* Function Name: CreateGC + * Description: Creates the GC's + * Arguments: w - the strip chart widget. + * which - which GC's to create. + * Returns: none + */ + +static void +CreateGC(w, which) +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); + } +} + +/* Function Name: DestroyGC + * Description: Destroys the GC's + * Arguments: w - the strip chart widget. + * which - which GC's to destroy. + * Returns: none + */ + +static void +DestroyGC(w, which) +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); +} + +/* ARGSUSED */ +static void Initialize (greq, gnew) + Widget greq, gnew; +{ + 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, (unsigned int) 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; + SetPoints(w); +} + +static void Destroy (gw) + 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, (unsigned int) ALL_GCS); +} + +/* + * NOTE: This function really needs to recieve graphics exposure + * events, but since this is not easily supported until R4 I am + * going to hold off until then. + */ + +/* ARGSUSED */ +static void Redisplay(w, event, region) + 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); +} + +/* ARGSUSED */ +static void +draw_it(client_data, id) +XtPointer client_data; +XtIntervalId *id; /* unused */ +{ + 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 >= (int)w->core.width) + MoveChart( (StripChartWidget) w, TRUE); + + /* Get the value, stash the point and draw corresponding line. */ + + if (w->strip_chart.get_value == NULL) + return; + + XtCallCallbacks( (Widget)w, XtNgetValue, (XtPointer)&value ); + + /* + * Keep w->strip_chart.max_value up to date, and if this data + * point is off the graph, change the scale to make it fit. + */ + + if (value > w->strip_chart.max_value) { + w->strip_chart.max_value = value; + if (w->strip_chart.max_value > w->strip_chart.scale) { + XClearWindow( XtDisplay (w), XtWindow (w)); + w->strip_chart.interval = repaint_window(w, 0, (int) w->core.width); + } + } + + w->strip_chart.valuedata[w->strip_chart.interval] = value; + if (XtIsRealized((Widget)w)) { + int y = (int) (w->core.height + - (int)(w->core.height * value) / w->strip_chart.scale); + + XFillRectangle(XtDisplay(w), XtWindow(w), w->strip_chart.fgGC, + w->strip_chart.interval, y, + (unsigned int) 1, w->core.height - y); + /* + * Fill in the graph lines we just painted over. + */ + + 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)); /* Flush output buffers */ + } + w->strip_chart.interval++; /* Next point */ +} /* draw_it */ + +/* Blts data according to current size, then redraws the stripChart window. + * Next represents the number of valid points in data. Returns the (possibly) + * adjusted value of next. If next is 0, this routine draws an empty window + * (scale - 1 lines for graph). If next is less than the current window width, + * the returned value is identical to the initial value of next and data is + * unchanged. Otherwise keeps half a window's worth of data. If data is + * changed, then w->strip_chart.max_value is updated to reflect the + * largest data point. + */ + +/* static int */ +int /* PH hack */ +repaint_window(w, left, width) +StripChartWidget w; +int left, width; +{ + register int i, j; + register int next = w->strip_chart.interval; + int scale = w->strip_chart.scale; + int scalewidth = 0; + + /* Compute the minimum scale required to graph the data, but don't go + lower than min_scale. */ + if (w->strip_chart.interval != 0 || scale <= (int)w->strip_chart.max_value) + scale = ((int) (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) { */ + + if (scale != w->strip_chart.scale && scale == 10) { + w->strip_chart.scale = scale; + left = 0; + width = next; + scalewidth = w->core.width; + + SetPoints(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; + + /* Draw data point lines. */ + for (i = left; i < width; i++) { + int y = (int) (w->core.height - + (int)(w->core.height * w->strip_chart.valuedata[i]) / + w->strip_chart.scale); + + XFillRectangle(dpy, win, w->strip_chart.fgGC, + i, y, (unsigned int) 1, + (unsigned int) (w->core.height - y)); + } + + /* Draw graph reference lines */ + for (i = 1; i < w->strip_chart.scale; i++) { + j = i * ((int)w->core.height / w->strip_chart.scale); + XDrawLine(dpy, win, w->strip_chart.hiGC, left, j, scalewidth, j); + } + } + return(next); +} + +/* Function Name: MoveChart + * Description: moves the chart over when it would run off the end. + * Arguments: w - the load widget. + * blit - blit the bits? (TRUE/FALSE). + * Returns: none. + */ + +static void +MoveChart(StripChartWidget w, Boolean blit) +{ + double old_max; + int left, i, j; + register int next = w->strip_chart.interval; + + if (!XtIsRealized((Widget) w)) return; + + if (w->strip_chart.jump_val == DEFAULT_JUMP) + j = w->core.width >> 1; /* Half the window width. */ + else { + j = w->core.width - w->strip_chart.jump_val; + if (j < 0) j = 0; + } + + bcopy((char *)(w->strip_chart.valuedata + next - j), + (char *)(w->strip_chart.valuedata), j * sizeof(double)); + next = w->strip_chart.interval = j; + + /* + * Since we just lost some data, recompute the + * w->strip_chart.max_value. + */ + + 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; /* we are done... */ + + if ( ((int) old_max) != ( (int) w->strip_chart.max_value) ) { + XClearWindow(XtDisplay(w), XtWindow(w)); + repaint_window(w, 0, (int) w->core.width); + return; + } + + XCopyArea(XtDisplay((Widget)w), XtWindow((Widget)w), XtWindow((Widget)w), + w->strip_chart.hiGC, (int) w->core.width - j, 0, + (unsigned int) j, (unsigned int) w->core.height, + 0, 0); + + XClearArea(XtDisplay((Widget)w), XtWindow((Widget)w), + (int) j, 0, + (unsigned int) w->core.width - j, (unsigned int)w->core.height, + FALSE); + + /* Draw graph reference lines */ + left = j; + for (i = 1; i < w->strip_chart.scale; i++) { + j = i * ((int)w->core.height / w->strip_chart.scale); + XDrawLine(XtDisplay((Widget) w), XtWindow( (Widget) w), + w->strip_chart.hiGC, left, j, (int)w->core.width, j); + } + return; +} + +/* ARGSUSED */ +static Boolean SetValues (current, request, new) + Widget current, request, new; +{ + StripChartWidget old = (StripChartWidget)current; + StripChartWidget w = (StripChartWidget)new; + Boolean 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(new), + w->strip_chart.update * MS_PER_SEC, + draw_it, (XtPointer)w); + } + + if ( w->strip_chart.min_scale > (int) ((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 ); +} + +/* Function Name: SetPoints + * Description: Sets up the polypoint that will be used to draw in + * the graph lines. + * Arguments: w - the StripChart widget. + * Returns: none. + */ + +#define HEIGHT ( (unsigned int) w->core.height) + +static void +SetPoints(w) +StripChartWidget w; +{ + XPoint * points; + Cardinal size; + int i; + + if (w->strip_chart.scale <= 1) { /* no scale lines. */ + 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; + + /* Draw graph reference lines into clip mask */ + + for (i = 1; i < w->strip_chart.scale; i++) { + points[i - 1].x = 0; + points[i - 1].y = HEIGHT / w->strip_chart.scale; + } +}