1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /***********************************************************
3 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts,
4 and the Massachusetts Institute of Technology, Cambridge, Massachusetts.
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.
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
24 ******************************************************************/
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.
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.
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". */
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>
47 #define MS_PER_SEC 1000
51 #define offset(field) XtOffsetOf(StripChartRec, field)
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},
74 /* Added argument types to these to shut picky compilers up. PH */
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();
83 int repaint_window(StripChartWidget, int, int); /* PH hack */
84 /* static int repaint_window(); */
86 StripChartClassRec stripChartClassRec = {
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,
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,
118 /* query_geometry */ XtInheritQueryGeometry,
119 /* display_accelerator */ XtInheritDisplayAccelerator,
122 { /* Simple class fields */
123 /* change_sensitive */ XtInheritChangeSensitive
127 WidgetClass mystripChartWidgetClass = (WidgetClass) &stripChartClassRec;
129 /****************************************************************
133 ****************************************************************/
135 static void draw_it();
137 /* Function Name: CreateGC
138 * Description: Creates the GC's
139 * Arguments: w - the strip chart widget.
140 * which - which GC's to create.
151 if (which & FOREGROUND) {
152 myXGCV.foreground = w->strip_chart.fgpixel;
153 w->strip_chart.fgGC = XtGetGC((Widget) w, GCForeground, &myXGCV);
156 if (which & HIGHLIGHT) {
157 myXGCV.foreground = w->strip_chart.hipixel;
158 w->strip_chart.hiGC = XtGetGC((Widget) w, GCForeground, &myXGCV);
162 /* Function Name: DestroyGC
163 * Description: Destroys the GC's
164 * Arguments: w - the strip chart widget.
165 * which - which GC's to destroy.
174 if (which & FOREGROUND)
175 XtReleaseGC((Widget) w, w->strip_chart.fgGC);
177 if (which & HIGHLIGHT)
178 XtReleaseGC((Widget) w, w->strip_chart.hiGC);
182 static void Initialize (greq, gnew)
185 StripChartWidget w = (StripChartWidget)gnew;
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);
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;
201 static void Destroy (gw)
204 StripChartWidget w = (StripChartWidget)gw;
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);
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.
220 static void Redisplay(w, event, region)
225 if (event->type == GraphicsExpose)
226 (void) repaint_window ((StripChartWidget)w, event->xgraphicsexpose.x,
227 event->xgraphicsexpose.width);
229 (void) repaint_window ((StripChartWidget)w, event->xexpose.x,
230 event->xexpose.width);
235 draw_it(client_data, id)
236 XtPointer client_data;
237 XtIntervalId *id; /* unused */
239 StripChartWidget w = (StripChartWidget)client_data;
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);
247 if (w->strip_chart.interval >= (int)w->core.width)
248 MoveChart( (StripChartWidget) w, TRUE);
250 /* Get the value, stash the point and draw corresponding line. */
252 if (w->strip_chart.get_value == NULL)
255 XtCallCallbacks( (Widget)w, XtNgetValue, (XtPointer)&value );
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.
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);
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);
275 XFillRectangle(XtDisplay(w), XtWindow(w), w->strip_chart.fgGC,
276 w->strip_chart.interval, y,
277 (unsigned int) 1, w->core.height - y);
279 * Fill in the graph lines we just painted over.
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,
289 XFlush(XtDisplay(w)); /* Flush output buffers */
291 w->strip_chart.interval++; /* Next point */
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.
306 repaint_window(w, left, width)
311 register int next = w->strip_chart.interval;
312 int scale = w->strip_chart.scale;
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;
322 /* if (scale != w->strip_chart.scale) { */
324 if (scale != w->strip_chart.scale && scale == 10) {
325 w->strip_chart.scale = scale;
328 scalewidth = w->core.width;
332 if (XtIsRealized ((Widget) w))
333 XClearWindow (XtDisplay (w), XtWindow (w));
337 if (XtIsRealized((Widget)w)) {
338 Display *dpy = XtDisplay(w);
339 Window win = XtWindow(w);
342 if (!scalewidth) scalewidth = width;
344 if (next < ++width) width = next;
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);
352 XFillRectangle(dpy, win, w->strip_chart.fgGC,
353 i, y, (unsigned int) 1,
354 (unsigned int) (w->core.height - y));
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);
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).
374 MoveChart(StripChartWidget w, Boolean blit)
378 register int next = w->strip_chart.interval;
380 if (!XtIsRealized((Widget) w)) return;
382 if (w->strip_chart.jump_val == DEFAULT_JUMP)
383 j = w->core.width >> 1; /* Half the window width. */
385 j = w->core.width - w->strip_chart.jump_val;
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;
394 * Since we just lost some data, recompute the
395 * w->strip_chart.max_value.
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];
405 if (!blit) return; /* we are done... */
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);
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,
418 XClearArea(XtDisplay((Widget)w), XtWindow((Widget)w),
420 (unsigned int) w->core.width - j, (unsigned int)w->core.height,
423 /* Draw graph reference lines */
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);
434 static Boolean SetValues (current, request, new)
435 Widget current, request, new;
437 StripChartWidget old = (StripChartWidget)current;
438 StripChartWidget w = (StripChartWidget)new;
439 Boolean ret_val = FALSE;
440 unsigned int new_gc = NO_GCS;
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);
452 if ( w->strip_chart.min_scale > (int) ((w->strip_chart.max_value) + 1) )
455 if ( w->strip_chart.fgpixel != old->strip_chart.fgpixel ) {
456 new_gc |= FOREGROUND;
460 if ( w->strip_chart.hipixel != old->strip_chart.hipixel ) {
465 DestroyGC(old, new_gc);
471 /* Function Name: SetPoints
472 * Description: Sets up the polypoint that will be used to draw in
474 * Arguments: w - the StripChart widget.
478 #define HEIGHT ( (unsigned int) w->core.height)
488 if (w->strip_chart.scale <= 1) { /* no scale lines. */
489 XtFree ((char *) w->strip_chart.points);
490 w->strip_chart.points = NULL;
494 size = sizeof(XPoint) * (w->strip_chart.scale - 1);
496 points = (XPoint *) XtRealloc( (XtPointer) w->strip_chart.points, size);
497 w->strip_chart.points = points;
499 /* Draw graph reference lines into clip mask */
501 for (i = 1; i < w->strip_chart.scale; i++) {
503 points[i - 1].y = HEIGHT / w->strip_chart.scale;