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