Fix broken comment.
[users/jgh/exim.git] / src / exim_monitor / em_StripChart.c
1 /* $Cambridge: exim/src/exim_monitor/em_StripChart.c,v 1.1 2004/10/07 10:39:01 ph10 Exp $ */
2 /* $XConsortium: StripChart.c,v 1.20 91/05/24 17:20:42 converse Exp $ */
3
4 /***********************************************************
5 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts,
6 and the Massachusetts Institute of Technology, Cambridge, Massachusetts.
7
8                         All Rights Reserved
9
10 Permission to use, copy, modify, and distribute this software and its
11 documentation for any purpose and without fee is hereby granted,
12 provided that the above copyright notice appear in all copies and that
13 both that copyright notice and this permission notice appear in
14 supporting documentation, and that the names of Digital or MIT not be
15 used in advertising or publicity pertaining to distribution of the
16 software without specific, written prior permission.
17
18 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
19 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
20 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
21 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
22 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
23 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
24 SOFTWARE.
25
26 ******************************************************************/
27
28 /* This is the Athena StripChart widget, slightly hacked by
29 Philip Hazel <ph10@cus.cam.ac.uk> in order to give access to
30 its repaint_window function so that a repaint can be forced.
31
32 The repaint_window function has also been nobbled so that it only
33 ever changes scale to 10. There is probably a better way to handle
34 this - such as inventing some new resources, but I'm not up to
35 that just at the moment.
36
37 On SunOS4 there are name clashes when trying to link this with the
38 Athena library. So to avoid them, rename a few things by inserting
39 "my" at the front of "strip". */
40
41
42 #include <stdio.h>
43 #include <X11/IntrinsicP.h>
44 #include <X11/StringDefs.h>
45 #include <X11/Xaw/XawInit.h>
46 #include <X11/Xaw/StripCharP.h>
47 #include <X11/Xfuncs.h>
48
49 #define MS_PER_SEC 1000
50
51 /* Private Data */
52
53 #define offset(field) XtOffsetOf(StripChartRec, field)
54
55 static XtResource resources[] = {
56     {XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension),
57         offset(core.width), XtRImmediate, (XtPointer) 120},
58     {XtNheight, XtCHeight, XtRDimension, sizeof(Dimension),
59         offset(core.height), XtRImmediate, (XtPointer) 120},
60     {XtNupdate, XtCInterval, XtRInt, sizeof(int),
61         offset(strip_chart.update), XtRImmediate, (XtPointer) 10},
62     {XtNminScale, XtCScale, XtRInt, sizeof(int),
63         offset(strip_chart.min_scale), XtRImmediate, (XtPointer) 1},
64     {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
65         offset(strip_chart.fgpixel), XtRString, XtDefaultForeground},
66     {XtNhighlight, XtCForeground, XtRPixel, sizeof(Pixel),
67         offset(strip_chart.hipixel), XtRString, XtDefaultForeground},
68     {XtNgetValue, XtCCallback, XtRCallback, sizeof(XtPointer),
69         offset(strip_chart.get_value), XtRImmediate, (XtPointer) NULL},
70     {XtNjumpScroll, XtCJumpScroll, XtRInt, sizeof(int),
71         offset(strip_chart.jump_val), XtRImmediate, (XtPointer) DEFAULT_JUMP},
72 };
73
74 #undef offset
75
76 /* Added argument types to these to shut picky compilers up. PH */
77
78 static void CreateGC(StripChartWidget, unsigned int);
79 static void DestroyGC(StripChartWidget, unsigned int);
80 static void Initialize(), Destroy(), Redisplay();
81 static void MoveChart(StripChartWidget, Boolean);
82 static void SetPoints(StripChartWidget);
83 static Boolean SetValues();
84
85 int repaint_window(StripChartWidget, int, int);     /* PH hack */
86 /* static int repaint_window(); */
87
88 StripChartClassRec stripChartClassRec = {
89     { /* core fields */
90     /* superclass               */      (WidgetClass) &simpleClassRec,
91     /* class_name               */      "StripChart",
92     /* size                     */      sizeof(StripChartRec),
93     /* class_initialize         */      XawInitializeWidgetSet,
94     /* class_part_initialize    */      NULL,
95     /* class_inited             */      FALSE,
96     /* initialize               */      Initialize,
97     /* initialize_hook          */      NULL,
98     /* realize                  */      XtInheritRealize,
99     /* actions                  */      NULL,
100     /* num_actions              */      0,
101     /* resources                */      resources,
102     /* num_resources            */      XtNumber(resources),
103     /* xrm_class                */      NULLQUARK,
104     /* compress_motion          */      TRUE,
105     /* compress_exposure        */      XtExposeCompressMultiple |
106                                         XtExposeGraphicsExposeMerged,
107     /* compress_enterleave      */      TRUE,
108     /* visible_interest         */      FALSE,
109     /* destroy                  */      Destroy,
110     /* resize                   */      (void (*)(Widget))SetPoints,
111     /* expose                   */      Redisplay,
112     /* set_values               */      SetValues,
113     /* set_values_hook          */      NULL,
114     /* set_values_almost        */      NULL,
115     /* get_values_hook          */      NULL,
116     /* accept_focus             */      NULL,
117     /* version                  */      XtVersion,
118     /* callback_private         */      NULL,
119     /* tm_table                 */      NULL,
120     /* query_geometry           */      XtInheritQueryGeometry,
121     /* display_accelerator      */      XtInheritDisplayAccelerator,
122     /* extension                */      NULL
123     },
124     { /* Simple class fields */
125     /* change_sensitive         */      XtInheritChangeSensitive
126     }
127 };
128
129 WidgetClass mystripChartWidgetClass = (WidgetClass) &stripChartClassRec;
130
131 /****************************************************************
132  *
133  * Private Procedures
134  *
135  ****************************************************************/
136
137 static void draw_it();
138
139 /*      Function Name: CreateGC
140  *      Description: Creates the GC's
141  *      Arguments: w - the strip chart widget.
142  *                 which - which GC's to create.
143  *      Returns: none
144  */
145
146 static void
147 CreateGC(w, which)
148 StripChartWidget w;
149 unsigned int which;
150 {
151   XGCValues     myXGCV;
152
153   if (which & FOREGROUND) {
154     myXGCV.foreground = w->strip_chart.fgpixel;
155     w->strip_chart.fgGC = XtGetGC((Widget) w, GCForeground, &myXGCV);
156   }
157
158   if (which & HIGHLIGHT) {
159     myXGCV.foreground = w->strip_chart.hipixel;
160     w->strip_chart.hiGC = XtGetGC((Widget) w, GCForeground, &myXGCV);
161   }
162 }
163
164 /*      Function Name: DestroyGC
165  *      Description: Destroys the GC's
166  *      Arguments: w - the strip chart widget.
167  *                 which - which GC's to destroy.
168  *      Returns: none
169  */
170
171 static void
172 DestroyGC(w, which)
173 StripChartWidget w;
174 unsigned int which;
175 {
176   if (which & FOREGROUND)
177     XtReleaseGC((Widget) w, w->strip_chart.fgGC);
178
179   if (which & HIGHLIGHT)
180     XtReleaseGC((Widget) w, w->strip_chart.hiGC);
181 }
182
183 /* ARGSUSED */
184 static void Initialize (greq, gnew)
185     Widget greq, gnew;
186 {
187     StripChartWidget w = (StripChartWidget)gnew;
188
189     if (w->strip_chart.update > 0)
190         w->strip_chart.interval_id = XtAppAddTimeOut(
191                                         XtWidgetToApplicationContext(gnew),
192                                         w->strip_chart.update * MS_PER_SEC,
193                                         draw_it, (XtPointer) gnew);
194     CreateGC(w, (unsigned int) ALL_GCS);
195
196     w->strip_chart.scale = w->strip_chart.min_scale;
197     w->strip_chart.interval = 0;
198     w->strip_chart.max_value = 0.0;
199     w->strip_chart.points = NULL;
200     SetPoints(w);
201 }
202
203 static void Destroy (gw)
204      Widget gw;
205 {
206      StripChartWidget w = (StripChartWidget)gw;
207
208      if (w->strip_chart.update > 0)
209          XtRemoveTimeOut (w->strip_chart.interval_id);
210      if (w->strip_chart.points)
211          XtFree((char *) w->strip_chart.points);
212      DestroyGC(w, (unsigned int) ALL_GCS);
213 }
214
215 /*
216  * NOTE: This function really needs to recieve graphics exposure
217  *       events, but since this is not easily supported until R4 I am
218  *       going to hold off until then.
219  */
220
221 /* ARGSUSED */
222 static void Redisplay(w, event, region)
223      Widget w;
224      XEvent *event;
225      Region region;
226 {
227     if (event->type == GraphicsExpose)
228         (void) repaint_window ((StripChartWidget)w, event->xgraphicsexpose.x,
229                                event->xgraphicsexpose.width);
230     else
231         (void) repaint_window ((StripChartWidget)w, event->xexpose.x,
232                                event->xexpose.width);
233 }
234
235 /* ARGSUSED */
236 static void
237 draw_it(client_data, id)
238 XtPointer client_data;
239 XtIntervalId *id;               /* unused */
240 {
241    StripChartWidget w = (StripChartWidget)client_data;
242    double value;
243
244    if (w->strip_chart.update > 0)
245        w->strip_chart.interval_id =
246        XtAppAddTimeOut(XtWidgetToApplicationContext( (Widget) w),
247                        w->strip_chart.update * MS_PER_SEC,draw_it,client_data);
248
249    if (w->strip_chart.interval >= (int)w->core.width)
250        MoveChart( (StripChartWidget) w, TRUE);
251
252    /* Get the value, stash the point and draw corresponding line. */
253
254    if (w->strip_chart.get_value == NULL)
255        return;
256
257    XtCallCallbacks( (Widget)w, XtNgetValue, (XtPointer)&value );
258
259    /*
260     * Keep w->strip_chart.max_value up to date, and if this data
261     * point is off the graph, change the scale to make it fit.
262     */
263
264    if (value > w->strip_chart.max_value) {
265        w->strip_chart.max_value = value;
266        if (w->strip_chart.max_value > w->strip_chart.scale) {
267            XClearWindow( XtDisplay (w), XtWindow (w));
268            w->strip_chart.interval = repaint_window(w, 0, (int) w->core.width);
269        }
270    }
271
272    w->strip_chart.valuedata[w->strip_chart.interval] = value;
273    if (XtIsRealized((Widget)w)) {
274        int y = (int) (w->core.height
275                       - (int)(w->core.height * value) / w->strip_chart.scale);
276
277        XFillRectangle(XtDisplay(w), XtWindow(w), w->strip_chart.fgGC,
278                       w->strip_chart.interval, y,
279                       (unsigned int) 1, w->core.height - y);
280        /*
281         * Fill in the graph lines we just painted over.
282         */
283
284        if (w->strip_chart.points != NULL) {
285            w->strip_chart.points[0].x = w->strip_chart.interval;
286            XDrawPoints(XtDisplay(w), XtWindow(w), w->strip_chart.hiGC,
287                        w->strip_chart.points, w->strip_chart.scale - 1,
288                        CoordModePrevious);
289        }
290
291        XFlush(XtDisplay(w));                /* Flush output buffers */
292    }
293    w->strip_chart.interval++;               /* Next point */
294 } /* draw_it */
295
296 /* Blts data according to current size, then redraws the stripChart window.
297  * Next represents the number of valid points in data.  Returns the (possibly)
298  * adjusted value of next.  If next is 0, this routine draws an empty window
299  * (scale - 1 lines for graph).  If next is less than the current window width,
300  * the returned value is identical to the initial value of next and data is
301  * unchanged.  Otherwise keeps half a window's worth of data.  If data is
302  * changed, then w->strip_chart.max_value is updated to reflect the
303  * largest data point.
304  */
305
306 /* static int */
307 int              /* PH hack */
308 repaint_window(w, left, width)
309 StripChartWidget w;
310 int left, width;
311 {
312     register int i, j;
313     register int next = w->strip_chart.interval;
314     int scale = w->strip_chart.scale;
315     int scalewidth = 0;
316
317     /* Compute the minimum scale required to graph the data, but don't go
318        lower than min_scale. */
319     if (w->strip_chart.interval != 0 || scale <= (int)w->strip_chart.max_value)
320       scale = ((int) (w->strip_chart.max_value)) + 1;
321     if (scale < w->strip_chart.min_scale)
322       scale = w->strip_chart.min_scale;
323
324 /*    if (scale != w->strip_chart.scale) { */
325
326     if (scale != w->strip_chart.scale && scale == 10) {
327       w->strip_chart.scale = scale;
328       left = 0;
329       width = next;
330       scalewidth = w->core.width;
331
332       SetPoints(w);
333
334       if (XtIsRealized ((Widget) w))
335         XClearWindow (XtDisplay (w), XtWindow (w));
336
337     }
338
339     if (XtIsRealized((Widget)w)) {
340         Display *dpy = XtDisplay(w);
341         Window win = XtWindow(w);
342
343         width += left - 1;
344         if (!scalewidth) scalewidth = width;
345
346         if (next < ++width) width = next;
347
348         /* Draw data point lines. */
349         for (i = left; i < width; i++) {
350             int y = (int) (w->core.height -
351                            (int)(w->core.height * w->strip_chart.valuedata[i]) /
352                            w->strip_chart.scale);
353
354             XFillRectangle(dpy, win, w->strip_chart.fgGC,
355                            i, y, (unsigned int) 1,
356                            (unsigned int) (w->core.height - y));
357         }
358
359         /* Draw graph reference lines */
360         for (i = 1; i < w->strip_chart.scale; i++) {
361             j = i * ((int)w->core.height / w->strip_chart.scale);
362             XDrawLine(dpy, win, w->strip_chart.hiGC, left, j, scalewidth, j);
363         }
364     }
365     return(next);
366 }
367
368 /*      Function Name: MoveChart
369  *      Description: moves the chart over when it would run off the end.
370  *      Arguments: w - the load widget.
371  *                 blit - blit the bits? (TRUE/FALSE).
372  *      Returns: none.
373  */
374
375 static void
376 MoveChart(StripChartWidget w, Boolean blit)
377 {
378     double old_max;
379     int left, i, j;
380     register int next = w->strip_chart.interval;
381
382     if (!XtIsRealized((Widget) w)) return;
383
384     if (w->strip_chart.jump_val == DEFAULT_JUMP)
385         j = w->core.width >> 1; /* Half the window width. */
386     else {
387         j = w->core.width - w->strip_chart.jump_val;
388         if (j < 0) j = 0;
389     }
390
391     bcopy((char *)(w->strip_chart.valuedata + next - j),
392           (char *)(w->strip_chart.valuedata), j * sizeof(double));
393     next = w->strip_chart.interval = j;
394
395     /*
396      * Since we just lost some data, recompute the
397      * w->strip_chart.max_value.
398      */
399
400     old_max = w->strip_chart.max_value;
401     w->strip_chart.max_value = 0.0;
402     for (i = 0; i < next; i++) {
403       if (w->strip_chart.valuedata[i] > w->strip_chart.max_value)
404         w->strip_chart.max_value = w->strip_chart.valuedata[i];
405     }
406
407     if (!blit) return;          /* we are done... */
408
409     if ( ((int) old_max) != ( (int) w->strip_chart.max_value) ) {
410       XClearWindow(XtDisplay(w), XtWindow(w));
411       repaint_window(w, 0, (int) w->core.width);
412       return;
413     }
414
415     XCopyArea(XtDisplay((Widget)w), XtWindow((Widget)w), XtWindow((Widget)w),
416               w->strip_chart.hiGC, (int) w->core.width - j, 0,
417               (unsigned int) j, (unsigned int) w->core.height,
418               0, 0);
419
420     XClearArea(XtDisplay((Widget)w), XtWindow((Widget)w),
421                (int) j, 0,
422                (unsigned int) w->core.width - j, (unsigned int)w->core.height,
423                FALSE);
424
425     /* Draw graph reference lines */
426     left = j;
427     for (i = 1; i < w->strip_chart.scale; i++) {
428       j = i * ((int)w->core.height / w->strip_chart.scale);
429       XDrawLine(XtDisplay((Widget) w), XtWindow( (Widget) w),
430                 w->strip_chart.hiGC, left, j, (int)w->core.width, j);
431     }
432     return;
433 }
434
435 /* ARGSUSED */
436 static Boolean SetValues (current, request, new)
437     Widget current, request, new;
438 {
439     StripChartWidget old = (StripChartWidget)current;
440     StripChartWidget w = (StripChartWidget)new;
441     Boolean ret_val = FALSE;
442     unsigned int new_gc = NO_GCS;
443
444     if (w->strip_chart.update != old->strip_chart.update) {
445         if (old->strip_chart.update > 0)
446             XtRemoveTimeOut (old->strip_chart.interval_id);
447         if (w->strip_chart.update > 0)
448             w->strip_chart.interval_id =
449                 XtAppAddTimeOut(XtWidgetToApplicationContext(new),
450                                 w->strip_chart.update * MS_PER_SEC,
451                                 draw_it, (XtPointer)w);
452     }
453
454     if ( w->strip_chart.min_scale > (int) ((w->strip_chart.max_value) + 1) )
455       ret_val = TRUE;
456
457     if ( w->strip_chart.fgpixel != old->strip_chart.fgpixel ) {
458       new_gc |= FOREGROUND;
459       ret_val = True;
460     }
461
462     if ( w->strip_chart.hipixel != old->strip_chart.hipixel ) {
463       new_gc |= HIGHLIGHT;
464       ret_val = True;
465     }
466
467     DestroyGC(old, new_gc);
468     CreateGC(w, new_gc);
469
470     return( ret_val );
471 }
472
473 /*      Function Name: SetPoints
474  *      Description: Sets up the polypoint that will be used to draw in
475  *                   the graph lines.
476  *      Arguments: w - the StripChart widget.
477  *      Returns: none.
478  */
479
480 #define HEIGHT ( (unsigned int) w->core.height)
481
482 static void
483 SetPoints(w)
484 StripChartWidget w;
485 {
486     XPoint * points;
487     Cardinal size;
488     int i;
489
490     if (w->strip_chart.scale <= 1) { /* no scale lines. */
491         XtFree ((char *) w->strip_chart.points);
492         w->strip_chart.points = NULL;
493         return;
494     }
495
496     size = sizeof(XPoint) * (w->strip_chart.scale - 1);
497
498     points = (XPoint *) XtRealloc( (XtPointer) w->strip_chart.points, size);
499     w->strip_chart.points = points;
500
501     /* Draw graph reference lines into clip mask */
502
503     for (i = 1; i < w->strip_chart.scale; i++) {
504         points[i - 1].x = 0;
505         points[i - 1].y = HEIGHT / w->strip_chart.scale;
506     }
507 }