1 /**
2  * Copyright 1993 Network Computing Devices, Inc.
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and
5  * its documentation for any purpose is hereby granted without fee, provided
6  * that the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name Network Computing Devices, Inc. not be
9  * used in advertising or publicity pertaining to distribution of this
10  * software without specific, written prior permission.
11  *
12  * THIS SOFTWARE IS PROVIDED `AS-IS'.  NETWORK COMPUTING DEVICES, INC.,
13  * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT
14  * LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
15  * PARTICULAR PURPOSE, OR NONINFRINGEMENT.  IN NO EVENT SHALL NETWORK
16  * COMPUTING DEVICES, INC., BE LIABLE FOR ANY DAMAGES WHATSOEVER, INCLUDING
17  * SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, INCLUDING LOSS OF USE, DATA,
18  * OR PROFITS, EVEN IF ADVISED OF THE POSSIBILITY THEREOF, AND REGARDLESS OF
19  * WHETHER IN AN ACTION IN CONTRACT, TORT OR NEGLIGENCE, ARISING OUT OF OR IN
20  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  *
22  * Author:	Greg Renda <greg@ncd.com>
23  * 		Network Computing Devices, Inc.
24  * 		350 North Bernardo Ave.
25  * 		Mountain View, CA  94043
26  *
27  * $NCDId: @(#)Graph.c,v 1.11 1995/12/06 01:11:03 greg Exp $
28  */
29 
30 #include "config.h"
31 #include <inttypes.h>
32 
33 #if defined(HAVE_LIMITS_H)
34 # include <limits.h>
35 #  define MAXSHORT SHRT_MAX
36 #  define MINSHORT SHRT_MIN
37 #elif defined(HAVE_VALUES_H)
38 # include <values.h>
39 #endif
40 
41 #if !defined(MAXSHORT)
42 # define MAXSHORT 0x7fff
43 #endif
44 
45 #if !defined(MINSHORT)
46 # define MINSHORT -MAXSHORT
47 #endif
48 
49 #include <X11/IntrinsicP.h>
50 #include <X11/StringDefs.h>
51 #include "GraphP.h"
52 
53 #define min(a, b) ((a) < (b) ? (a) : (b))
54 #define max(a, b) ((a) > (b) ? (a) : (b))
55 #define abs(a) ((a) < 0 ? -(a) : (a))
56 
57 #define offset(field) XtOffsetOf(GraphRec, graph.field)
58 /* {name, class, type, size, offset, default_type, default_addr}, */
59 static XtResource resources[] =
60 {
61     XtNgraphColor, XtCColor, XtRPixel, sizeof(Pixel), offset(graphColor),
62     XtRString, XtDefaultForeground,
63 
64     XtNmarkerColor, XtCColor, XtRPixel, sizeof(Pixel), offset(markerColor),
65     XtRString, "red",
66 
67     XtNpositionColor, XtCColor, XtRPixel, sizeof(Pixel), offset(positionColor),
68     XtRString, "blue",
69 
70     XtNdata, XtCData, XtRPointer, sizeof(int *), offset(data), XtRImmediate,
71     NULL,
72 
73     XtNnumSamples, XtCNumSamples, XtRInt, sizeof(int), offset(numSamples),
74     XtRImmediate, 0,
75 
76     XtNnumTracks, XtCNumTracks, XtRInt, sizeof(int), offset(numTracks),
77     XtRImmediate, (XtPointer) 1,
78 
79     XtNstart, XtCStart, XtRInt, sizeof(int), offset(start), XtRImmediate, 0,
80 
81     XtNend, XtCEnd, XtRInt, sizeof(int), offset(end), XtRImmediate, 0,
82 
83     XtNleftMarker, XtCLeftMarker, XtRInt, sizeof(int), offset(leftMarker),
84     XtRImmediate, 0,
85 
86     XtNrightMarker, XtCRightMarker, XtRInt, sizeof(int), offset(rightMarker),
87     XtRImmediate, 0,
88 
89     XtNposition, XtCPosition, XtRInt, sizeof(int), offset(position),
90     XtRImmediate, 0,
91 
92     XtNleftProc, XtCCallback, XtRCallback, sizeof(XtPointer), offset(leftProc),
93     XtRCallback, NULL,
94 
95     XtNrightProc, XtCCallback, XtRCallback, sizeof(XtPointer),
96     offset(rightProc), XtRCallback, NULL,
97 };
98 #undef offset
99 
100 static void     selectMarker(), moveMarker();
101 
102 static XtActionsRec actions[] =
103 {
104     /* {name, procedure}, */
105     {"selectMarker", selectMarker},
106     {"moveMarker", moveMarker},
107 };
108 
109 static char     translations[] =
110 "\
111 <BtnDown>:      selectMarker()\n\
112 <BtnMotion>:    moveMarker()\n\
113 ";
114 
115 static void     Initialize(), Exposure(), Destroy(), Resize();
116 static Boolean  SetValues(), redrawPending;
117 
118 GraphClassRec   graphClassRec =
119 {
120     {						/* core fields */
121 	(WidgetClass) & widgetClassRec,		/* superclass */
122 	"Graph",				/* class_name */
123 	sizeof(GraphRec),			/* widget_size */
124 	NULL,					/* class_initialize */
125 	NULL,					/* class_part_initialize */
126 	FALSE,					/* class_inited */
127 	Initialize,				/* initialize */
128 	NULL,					/* initialize_hook */
129 	XtInheritRealize,			/* realize */
130 	actions,				/* actions */
131 	XtNumber(actions),			/* num_actions */
132 	resources,				/* resources */
133 	XtNumber(resources),			/* num_resources */
134 	NULLQUARK,				/* xrm_class */
135 	TRUE,					/* compress_motion */
136 	TRUE,					/* compress_exposure */
137 	TRUE,					/* compress_enterleave */
138 	FALSE,					/* visible_interest */
139 	Destroy,				/* destroy */
140 	Resize,					/* resize */
141 	Exposure,				/* expose */
142 	SetValues,				/* set_values */
143 	NULL,					/* set_values_hook */
144 	XtInheritSetValuesAlmost,		/* set_values_almost */
145 	NULL,					/* get_values_hook */
146 	NULL,					/* accept_focus */
147 	XtVersion,				/* version */
148 	NULL,					/* callback_private */
149 	translations,				/* tm_table */
150 	XtInheritQueryGeometry,			/* query_geometry */
151 	XtInheritDisplayAccelerator,		/* display_accelerator */
152 	NULL,					/* extension */
153     },
154     {						/* graph fields */
155 	0					/* empty */
156     }
157 };
158 
159 WidgetClass     graphWidgetClass = (WidgetClass) & graphClassRec;
160 
161 static void
CreateGC(w,which)162 CreateGC(w, which)
163 GraphWidget     w;
164 unsigned int    which;
165 {
166     XGCValues       gcv;
167 #if !defined(__alpha__) && !defined(__x86_64__) && !defined(__ia64__) && !defined(__LP64__)
168     unsigned long   commonMask;
169 #else /* __alpha */
170     unsigned int  commonMask;
171 #endif /* __alpha */
172 
173     gcv.graphics_exposures = FALSE;
174     commonMask = GCGraphicsExposures;
175 
176     if (which & GRAPH_GC)
177     {
178 	gcv.foreground = w->graph.graphColor;
179 	w->graph.graphGC = XtGetGC((Widget) w, commonMask | GCForeground, &gcv);
180     }
181 
182     if (which & POSITION_GC)
183     {
184 	gcv.foreground = w->graph.positionColor ^ w->core.background_pixel;
185 	gcv.function = GXxor;
186 	gcv.line_style = LineOnOffDash;
187 	w->graph.positionGC = XtGetGC((Widget) w,
188 				   commonMask | GCForeground | GCLineStyle |
189 				      GCFunction, &gcv);
190     }
191 
192     if (which & MARKER_GC)
193     {
194 	gcv.foreground = w->graph.markerColor ^ w->core.background_pixel;
195 	gcv.function = GXxor;
196 	w->graph.markerGC = XtGetGC((Widget) w,
197 				    commonMask | GCForeground | GCFunction,
198 				    &gcv);
199     }
200 }
201 
202 static void
DestroyGC(w,which)203 DestroyGC(w, which)
204 GraphWidget     w;
205 unsigned int    which;
206 {
207     if (which & GRAPH_GC)
208 	XtReleaseGC((Widget) w, w->graph.graphGC);
209 
210     if (which & POSITION_GC)
211 	XtReleaseGC((Widget) w, w->graph.positionGC);
212 
213     if (which & MARKER_GC)
214 	XtReleaseGC((Widget) w, w->graph.markerGC);
215 }
216 
217 /* ARGSUSED */
218 static void
Initialize(req,new)219 Initialize(req, new)
220 Widget          req,
221                 new;
222 {
223     CreateGC(new, (unsigned int) ALL_GCS);
224     redrawPending = FALSE;
225 }
226 
227 static void
Destroy(w)228 Destroy(w)
229 Widget          w;
230 {
231     DestroyGC(w, (unsigned int) ALL_GCS);
232 }
233 
234 static void
drawPosition(w)235 drawPosition(w)
236 GraphWidget     w;
237 {
238     int             x;
239 
240     if (w->graph.position >= w->graph.end)
241 	w->graph.position = w->graph.end - 1;
242 
243     if (w->graph.position < w->graph.start)
244 	w->graph.position = w->graph.start;
245 
246     x = (w->graph.position - w->graph.start) * w->graph.hscale;
247 
248     XDrawLine(XtDisplay(w), XtWindow(w), w->graph.positionGC, x, 0, x,
249 	      w->core.height - 1);
250 }
251 
252 static void
drawMarker(w,m)253 drawMarker(w, m)
254 GraphWidget     w;
255 int             m;
256 {
257     int            *x,
258                     marker;
259 
260     if (m == GraphLeftMarker)
261     {
262 	marker = w->graph.leftMarker;
263 
264 	if (marker >= w->graph.rightMarker)
265 	    marker = w->graph.rightMarker - 1;
266 
267 	if (marker < w->graph.start)
268 	    marker = w->graph.start;
269 
270 	w->graph.leftMarker = marker;
271 	x = &w->graph.leftMarkerX;
272     }
273     else
274     {				/* right marker */
275 	marker = w->graph.rightMarker;
276 
277 	if (marker <= w->graph.leftMarker)
278 	    marker = w->graph.leftMarker + 1;
279 
280 	if (marker > w->graph.end)
281 	    marker = w->graph.end;
282 
283 	w->graph.rightMarker = marker;
284 	x = &w->graph.rightMarkerX;
285     }
286 
287     *x = (marker - w->graph.start) * w->graph.hscale;
288 
289     if (w->graph.leftMarkerX == w->graph.rightMarkerX)
290 	*x += m == GraphLeftMarker ? -1 : 1;
291 
292     XDrawLine(XtDisplay(w), XtWindow(w), w->graph.markerGC, *x, 0, *x,
293 	      w->core.height - 1);
294 }
295 
296 static void
paintWindow(w)297 paintWindow(w)
298 GraphWidget     w;
299 {
300     Display        *dpy = XtDisplay(w);
301     Window          win = XtWindow(w);
302     int             x,
303                     t,
304                     center;
305     float           k;
306     GraphDataType  *p,
307                    *end;
308 
309     redrawPending = FALSE;
310     XClearWindow(dpy, win);
311 
312     if (w->graph.numSamples)
313 	for (t = 0; t < w->graph.numTracks; t++)
314 	{
315 	    center = (int) w->core.height / (int) w->graph.numTracks / 2 *
316 		((t + 1) * 2 - 1);
317 
318 	    /* draw center line */
319 	    XDrawLine(dpy, win, w->graph.graphGC, 0, center, w->core.width,
320 		      center);
321 
322 	    /* draw the data */
323 	    p = w->graph.data + (w->graph.start * w->graph.numTracks) + t;
324 	    end = w->graph.data + (w->graph.end * w->graph.numTracks);
325 	    k = w->graph.hscale;
326 
327 	    if (k < 1)
328 	    {			/* multiple samples per pixel */
329 		GraphDataType   minY,
330 		                maxY,
331 		                pminY,
332 		                pmaxY,
333 		                v;
334 
335 		pminY = pmaxY = 0;
336 
337 		for (x = 0; x < (int) w->core.width; x++)
338 		{
339 		    minY = MAXSHORT;
340 		    maxY = MINSHORT;
341 
342 		    for (; (int) k == x && p < end; k += w->graph.hscale)
343 		    {
344 			v = *p;
345 			p += w->graph.numTracks;
346 
347 			minY = min(v, minY);
348 			maxY = max(v, maxY);
349 		    }
350 
351 		    minY = min(pmaxY, minY);
352 		    maxY = max(pminY, maxY);
353 
354 		    pminY = minY;
355 		    pmaxY = maxY;
356 
357 		    XDrawLine(dpy, win, w->graph.graphGC,
358 			      x, (int) (-maxY * w->graph.vscale + center),
359 			      x, (int) (-minY * w->graph.vscale + center));
360 		}
361 	    }
362 	    else
363 	    {			/* multiple pixels per sample */
364 		int             px,
365 		                py,
366 		                y;
367 
368 		px = 0;
369 		py = -*p * w->graph.vscale + center;
370 		p += w->graph.numTracks;
371 		x = k;
372 		k += w->graph.hscale;
373 
374 		for (; p < end; k += w->graph.hscale, x = k)
375 		{
376 		    y = -*p * w->graph.vscale + center;
377 		    p += w->graph.numTracks;
378 		    XDrawLine(dpy, win, w->graph.graphGC, px, py, x, y);
379 		    px = x;
380 		    py = y;
381 		}
382 	    }
383 	}
384 
385     /* draw markers */
386     w->graph.rightMarkerX = w->core.width;
387 
388     drawMarker(w, GraphLeftMarker);
389     drawMarker(w, GraphRightMarker);
390     drawPosition(w);
391 }
392 
393 static void
Exposure(w,event,region)394 Exposure(w, event, region)
395 Widget          w;
396 XEvent         *event;
397 Region          region;
398 {
399     if (XtIsRealized((Widget) w))
400 	paintWindow(w);
401 }
402 
403 static void
recalc(w)404 recalc(w)
405 GraphWidget     w;
406 {
407     w->graph.vscale = (float) w->core.height / w->graph.numTracks /
408 	(MAXSHORT - MINSHORT + 1);
409 
410     w->graph.hscale = (float) ((int) w->core.width - 1) /
411 	(w->graph.end - w->graph.start);
412 }
413 
414 static void
validate(g)415 validate(g)
416 GraphPart      *g;
417 {
418     if (g->start < 0)
419 	g->start = 0;
420     else
421 	g->start = min(g->start, g->numSamples - 1);
422 
423     if (g->end <= g->start)
424 	g->end = g->start + 1;
425     else
426 	g->end = min(g->end, g->numSamples);
427 
428     if (g->leftMarker >= g->rightMarker)
429 	g->leftMarker = g->rightMarker - 1;
430 
431     if (g->leftMarker < g->start)
432 	g->leftMarker = g->start;
433 
434     if (g->rightMarker <= g->leftMarker)
435 	g->rightMarker = g->leftMarker + 1;
436 
437     if (g->rightMarker > g->end)
438 	g->rightMarker = g->end;
439 }
440 
441 /* ARGSUSED */
442 static          Boolean
SetValues(old,req,w)443 SetValues(old, req, w)
444 GraphWidget     old,
445                 req,
446                 w;
447 {
448     Boolean         newGC = NO_GCS,
449                     redraw = FALSE;
450 
451     validate(&w->graph);
452 
453     if (w->graph.leftMarker != old->graph.leftMarker)
454     {
455 	XtCallCallbacks((Widget) w, XtNleftProc,
456 			(XtPointer)(intptr_t)w->graph.leftMarker);
457 	redraw = TRUE;
458     }
459 
460     if (w->graph.rightMarker != old->graph.rightMarker)
461     {
462 	XtCallCallbacks((Widget) w, XtNrightProc,
463 			(XtPointer)(intptr_t)w->graph.rightMarker);
464 	redraw = TRUE;
465     }
466 
467     if (w->graph.position != old->graph.position ||
468 	w->graph.data != old->graph.data)
469 	redraw = TRUE;
470 
471     if (w->graph.numSamples != old->graph.numSamples ||
472 	w->graph.start != old->graph.start ||
473 	w->graph.end != old->graph.end)
474     {
475 	recalc(w);
476 	redraw = TRUE;
477     }
478 
479     if (w->graph.graphColor != old->graph.graphColor)
480 	newGC |= GRAPH_GC;
481 
482     if (w->graph.positionColor != old->graph.positionColor)
483 	newGC |= POSITION_GC;
484 
485     if (w->graph.markerColor != old->graph.markerColor)
486 	newGC |= MARKER_GC;
487 
488     if (w->core.background_pixel != old->core.background_pixel)
489 	newGC |= MARKER_GC;
490 
491     if (newGC)
492     {
493 	DestroyGC(old, newGC);
494 	CreateGC(w, newGC);
495 	redraw = TRUE;
496     }
497 
498     if (redrawPending)
499 	return FALSE;
500     else
501 	return redrawPending = redraw;
502 }
503 
504 static void
Resize(w)505 Resize(w)
506 Widget          w;
507 {
508     recalc(w);
509 }
510 
511 static void
selectMarker(w,event)512 selectMarker(w, event)
513 GraphWidget     w;
514 XButtonEvent   *event;
515 {
516     w->graph.marker = abs(event->x - w->graph.leftMarkerX) <
517 	abs(event->x - w->graph.rightMarkerX) ? GraphLeftMarker :
518 	GraphRightMarker;
519 
520     moveMarker(w, event);
521 }
522 
523 static void
moveMarker(w,event)524 moveMarker(w, event)
525 GraphWidget     w;
526 XButtonEvent   *event;
527 {
528     int             marker = event->x / w->graph.hscale + w->graph.start;
529 
530     /* erase the old marker */
531     drawMarker(w, w->graph.marker);
532 
533     if (w->graph.marker == GraphLeftMarker)
534 	w->graph.leftMarker = marker;
535     else
536 	w->graph.rightMarker = marker;
537 
538     /* draw the new marker */
539     drawMarker(w, w->graph.marker);
540 
541     if (w->graph.marker == GraphLeftMarker)
542 	XtCallCallbacks((Widget) w, XtNleftProc,
543 			(XtPointer)(intptr_t)w->graph.leftMarker);
544     else
545 	XtCallCallbacks((Widget) w, XtNrightProc,
546 			(XtPointer)(intptr_t)w->graph.rightMarker);
547 }
548 
549 /* public functions */
550 
551 void
GraphSetPosition(w,p)552 GraphSetPosition(w, p)
553 GraphWidget     w;
554 int             p;
555 {
556     /* erase the old position */
557     drawPosition(w);
558 
559     w->graph.position = p;
560 
561     /* draw the new position */
562     drawPosition(w);
563 }
564 
565 void
GraphRedraw(w)566 GraphRedraw(w)
567 GraphWidget     w;
568 {
569     paintWindow(w);
570 }
571