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