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