Commit | Line | Data |
---|---|---|
059ec3d9 PH |
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 | } |