g2-only: add (min,max) stats to the anomaly detector
[vpp.git] / src / tools / g2 / view1.c
1 /*
2  *------------------------------------------------------------------
3  * Copyright (c) 2005-2019 Cisco and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <errno.h>
20 #include <gtk/gtk.h>
21 #include <gdk/gdkkeysyms.h>
22 #include "g2.h"
23 #include <time.h>
24 #include <string.h>
25 #include <vppinfra/format.h>
26 #include <vppinfra/elog.h>
27 #include <math.h>
28
29 /*
30  * The main event display view.
31  *
32  * Important variables:
33  *
34  * "da" -- the drawing area, aka the screen representation of the
35  *         event view.
36  *
37  * "pm" -- the backing pixmap for the drawing area. Note that
38  *         all graphics operations target this backing
39  *         store, then call gtk_widget_draw to copy a rectangle from
40  *         the backing store onto the screen.
41  *
42  * "s_v1" -- pointer to the current v1_geometry_t object.
43  *
44  * Box heirarchy:
45  * s_view1_vbox
46  *     s_view1_hbox
47  *         da  s_view1_vmenubox
48  *                  s_view1_topbutton("Top")
49  *                  s_view1_vscroll (vertical scrollbar)
50  *                  s_view1_bottombutton("Bottom")
51  *     s_view1_hmenubox
52  *         s_view1_startbutton("Start");
53  *         s_view1_hscroll(horizontal scrollbar)
54  *         s_view1_endbutton("End")
55  *         s_view1_zoominbutton("Zoomin")
56  *         s_view1_searchbutton("Search")
57  *         s_view1_searchagainbutton("Search Again")
58  *         s_view1_zoomoutbutton("Zoomout")
59  *     s_view1_label
60  */
61
62 /*
63  * Globals
64  */
65
66 GdkFont *g_font;                /* a fixed-width font to use */
67 /* color format: 0 (for static colors), r (0-64k), g (0-64k), b (0-64k) */
68 GdkColor fg_black = {0, 0, 0, 0};
69 GdkColor fg_red   = {0, 65535, 0, 0};
70 GdkColor bg_white = {0, 65535, 65535, 65535};
71 static boolean summary_mode = TRUE; /* start out in summary mode */
72 static boolean color_mode   = FALSE; /* start out in monochrome mode   */
73
74 /*
75  * Locals
76  */
77
78 /*
79  * user_data values passed to view1_button_click_callback,
80  * which is used by the various action buttons noted above
81  */
82 enum view1_button_click {
83     TOP_BUTTON=1,
84     BOTTOM_BUTTON,
85     START_BUTTON,
86     ZOOMIN_BUTTON,
87     SEARCH_BUTTON,
88     ANOMALY_BUTTON,
89     ANOMALY_NEXT_BUTTON,
90     ANOMALY_THRESHOLD_BUTTON,
91     SEARCH_AGAIN_BUTTON,
92     ZOOMOUT_BUTTON,
93     END_BUTTON,
94     MORE_TRACES_BUTTON,
95     LESS_TRACES_BUTTON,
96     SNAP_BUTTON,
97     NEXT_BUTTON,
98     DEL_BUTTON,
99     CHASE_EVENT_BUTTON,
100     CHASE_DATUM_BUTTON,
101     CHASE_TRACK_BUTTON,
102     UNCHASE_BUTTON,
103     FORWARD_BUTTON,
104     BACKWARD_BUTTON,
105     SUMMARY_BUTTON,
106     NOSUMMARY_BUTTON,
107     SLEW_LEFT_BUTTON,
108     SLEW_RIGHT_BUTTON,
109 };
110
111 enum chase_mode {
112     CHASE_EVENT=1,
113     CHASE_DATUM,
114     CHASE_TRACK,
115 };
116
117 enum sc_dir {
118     SRCH_CHASE_FORWARD = 0,
119     SRCH_CHASE_BACKWARD = 1,
120 };
121
122 static GtkWidget *s_view1_hbox; /* see box heirarchy chart */
123 static GtkWidget *s_view1_vbox; /* see box heirarchy chart */
124 static GtkWidget *da;           /* main drawing area */
125 static GdkPixmap *pm;           /* and its backing pixmap */
126 static GdkCursor *norm_cursor;  /* the "normal" cursor */
127
128 /*
129  * view geometry parameters
130  *
131  * Remember:
132  *    Y increases down the page.
133  *    Strip origin is at the top
134  *    Payday is Friday
135  *    Don't put your fingers in your mouth.
136  *
137  * Most of these values are in pixels
138  */
139
140 typedef struct v1_geometry {
141     int pid_ax_width;           /* Width of the PID axis */
142     int time_ax_height;         /* Height of the time axis */
143     int time_ax_spacing;        /* TimeAxis: Space between tick-marks */
144     int strip_height;           /* Height of a regular PID trace */
145     int pop_offset;             /* Vertical offset of the detail box */
146     int pid_ax_offset;          /* Vertical offset of the PID axis */
147     int event_offset;           /* Vertical offset of the event boxes */
148     int total_height;           /* total height of da, see configure_event */
149     int total_width;            /* ditto, for width */
150     double last_time_interval;  /* last time interval, in f64 seconds */
151     double anomaly_threshold_stddevs; /* Anomaly detection threshold */
152
153     /* Derived values */
154     int first_pid_index;        /* Index of first displayed PID */
155     int npids;                  /* Max number of displayed pids */
156     ulonglong minvistime;       /* in usec */
157     ulonglong maxvistime;       /* in usec */
158
159     /* Anomaly detection statistics */
160     f64 *means, *variances, *two_stddevs;
161     f64 *mins, *maxes;
162     u32 *matches;
163
164 } v1_geometry_t;
165
166
167 /* The active geometry object */
168 static v1_geometry_t s_v1record;
169 static v1_geometry_t *s_v1 = &s_v1record;
170
171 /* The color array */
172 static GdkColor *s_color;
173
174 /* Snapshot ring */
175 typedef struct snapshot {
176     struct snapshot *next;
177     /* Screen geometry */
178     v1_geometry_t geometry;
179     boolean show_event[NEVENTS];
180     pid_sort_t *pidvec;
181     /*
182      * Note: not worth recomputing the vertical scrollbar, just save
183      *  its value here
184      */
185     gfloat vscroll_value;
186     boolean summary_mode;
187     boolean color_mode;
188 } snapshot_t;
189
190 static snapshot_t *s_snapshots;
191 static snapshot_t *s_cursnap;
192 static event_t *s_last_selected_event;
193
194 /*
195  * various widgets, see the box heirarchy chart above
196  * The toolkit keeps track of these things, we could lose many of
197  * these pointers.
198  */
199 static GtkWidget *s_view1_vmenubox;
200 static GtkWidget *s_view1_topbutton;
201 static GtkWidget *s_view1_bottombutton;
202 static GtkWidget *s_view1_more_traces_button;
203 static GtkWidget *s_view1_less_traces_button;
204
205 static GtkWidget *s_view1_hmenubox;
206 static GtkWidget *s_view1_hmenubox2;
207 static GtkWidget *s_view1_startbutton;
208 static GtkWidget *s_view1_zoominbutton;
209 static GtkWidget *s_view1_searchbutton;
210 static GtkWidget *s_view1_srchagainbutton;
211 static GtkWidget *s_view1_anomalybutton;
212 static GtkWidget *s_view1_anomalynextbutton;
213 static GtkWidget *s_view1_zoomoutbutton;
214 static GtkWidget *s_view1_endbutton;
215
216 static GtkWidget *s_view1_snapbutton;
217 static GtkWidget *s_view1_nextbutton;
218 static GtkWidget *s_view1_delbutton;
219
220 static GtkWidget *s_view1_chase_event_button;
221 static GtkWidget *s_view1_chase_datum_button;
222 static GtkWidget *s_view1_chase_track_button;
223 static GtkWidget *s_view1_unchasebutton;
224
225 static GtkWidget *s_view1_forward_button;
226 static GtkWidget *s_view1_backward_button;
227
228 static GtkWidget *s_view1_summary_button;
229 static GtkWidget *s_view1_nosummary_button;
230
231 static GtkWidget *s_view1_time_slew_right_button;
232 static GtkWidget *s_view1_time_slew_left_button;
233
234 static GtkWidget *s_view1_anomalythresholdbutton;
235
236 static GtkWidget *s_view1_hscroll;
237 static GtkObject *s_view1_hsadj;
238
239 static GtkWidget *s_view1_vscroll;
240 static GtkObject *s_view1_vsadj;
241
242 static GtkWidget *s_view1_label;
243
244 /*
245  * Search context
246  */
247 static ulong s_srchcode;        /* search event code */
248 static ulong s_anomalycode;     /* anomaly event code */
249 static int s_srchindex;         /* last hit was at this event index */
250 static boolean s_result_up;     /* The SEARCH RESULT dongle is displayed */
251 static boolean s_srchfail_up;   /* The status line "Search Failed" is up */
252 static int srch_chase_dir;      /* search/chase dir, 0=>forward */
253
254
255 /*
256  * Print context
257  */
258 static int s_print_offset;      /* Magic offset added to line, tbox fn codes */
259 static FILE *s_printfp;
260
261 /*
262  * Forward reference prototypes
263  */
264 static void display_pid_axis(v1_geometry_t *vp);
265 static void display_event_data(v1_geometry_t *vp);
266 static void display_time_axis(v1_geometry_t *vp);
267 static void view1_button_click_callback(GtkButton *item, gpointer data);
268
269 /*
270  * config params
271  */
272
273 gint c_view1_draw_width;
274 gint c_view1_draw_height;
275
276 /*
277  * Zoom-In / Time Ruler cursor
278  */
279
280 #define zi_width 32
281 #define zi_height 32
282 #define zi_x_hot 22
283 #define zi_y_hot 14
284 static unsigned char zi_bits[] = {
285    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
286    0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
287    0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
288    0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x88, 0x00,
289    0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xc0, 0x00,
290    0x00, 0xfc, 0xff, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xa0, 0x00,
291    0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x84, 0x00,
292    0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
293    0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
294    0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
295    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
296
297 static unsigned char zi_bkgd[] = {
298    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
299    0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
300    0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
301    0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x88, 0x00,
302    0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xc0, 0x00,
303    0x00, 0xfc, 0xff, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xa0, 0x00,
304    0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x84, 0x00,
305    0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
306    0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
307    0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
308    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
309
310 static GdkCursor *zi_cursor;
311 static GdkPixmap *zi_source, *zi_mask;
312
313 /*
314  * Frequently-used small computations, best
315  * done correctly once and instantiated.
316  */
317
318 /****************************************************************************
319 * dtime_per_pixel
320 ****************************************************************************/
321
322 static inline double dtime_per_pixel(v1_geometry_t *vp)
323 {
324     return ((double)(vp->maxvistime - vp->minvistime)) /
325         ((double)(vp->total_width - vp->pid_ax_width));
326 }
327
328 /****************************************************************************
329 * message_line
330 * Changes the status line.  Pass "" to clear the status line.
331 ****************************************************************************/
332
333 void message_line (char *s)
334 {
335     gtk_label_set_text (GTK_LABEL(s_view1_label), s);
336 }
337
338 /****************************************************************************
339 * set_window_title
340 * Changes the window title to include the specified filename.
341 ****************************************************************************/
342
343 void set_window_title (const char *filename)
344 {
345     char title[128];
346     snprintf(title, sizeof(title), "g2 (%s)", filename);
347     gtk_window_set_title(GTK_WINDOW(g_mainwindow), title);
348 }
349
350 /****************************************************************************
351 * recompute_hscrollbar
352 * Adjust the horizontal scrollbar's adjustment object.
353 *
354 * GtkAdjustments are really cool, but have to be set up exactly
355 * right or the various client objects screw up completely.
356 *
357 * Note: this function is *not* called when the user clicks the scrollbar.
358 ****************************************************************************/
359
360 static void recompute_hscrollbar (void)
361 {
362     ulonglong current_width;
363     ulonglong event_incdec;
364     GtkAdjustment *adj;
365     event_t *ep;
366
367     if (g_nevents == 0)
368         return;
369
370     ep = (g_events + (g_nevents-1));
371     current_width = s_v1->maxvistime - s_v1->minvistime;
372     event_incdec = (current_width) / 6;
373
374     adj = GTK_ADJUSTMENT(s_view1_hsadj);
375
376     /*
377      * Structure member decoder ring
378      * -----------------------------
379      * lower             the minimum possible value
380      * value             the current value
381      * upper             the maximum possible value
382      * step_increment    end button click increment
383      * page_increment    click in trough increment
384      * page_size         size of currently visible area
385      */
386
387     adj->lower = (gfloat)0.00;
388     adj->value = (gfloat)s_v1->minvistime;
389
390     /* Minor click: move about 1/6 of a page */
391     adj->step_increment = (gfloat)event_incdec;
392
393     /* Major click: move about 1/3 of a page. */
394     adj->page_increment = (gfloat)(2*event_incdec);
395
396     /* allow the user to go a bit past the end */
397     adj->upper = adj->page_increment/3 + (gfloat)(ep->time);
398     adj->page_size = (gfloat)(current_width);
399
400     /*
401      * Tell all clients (e.g. the visible scrollbar) to
402      * make themselves look right
403      */
404     gtk_adjustment_changed(adj);
405     gtk_adjustment_value_changed(adj);
406 }
407
408 /****************************************************************************
409 * recompute_vscrollbar
410 * Ditto, for the vertical scrollbar
411 ****************************************************************************/
412
413 static void recompute_vscrollbar (void)
414 {
415     GtkAdjustment *adj;
416
417     adj = GTK_ADJUSTMENT(s_view1_vsadj);
418
419     adj->lower = (gfloat)0.00;
420     adj->upper = (gfloat)g_npids;
421     adj->value = (gfloat)0.00;
422     adj->step_increment = 1.00;
423     adj->page_increment = (gfloat)(s_v1->npids / 3);
424     adj->page_size = (gfloat)s_v1->npids;
425     gtk_adjustment_changed(adj);
426     gtk_adjustment_value_changed(adj);
427 }
428
429 /****************************************************************************
430 * format_popbox_string
431 ****************************************************************************/
432
433 elog_main_t elog_main;
434
435 void format_popbox_string (char *tmpbuf, int len, event_t *ep, event_def_t *edp)
436 {
437     char *fp;
438
439 #ifdef NOTDEF
440     sprintf(tmpbuf,"%d:", ep->code);
441 #endif
442     if (ep->flags & EVENT_FLAG_CLIB) {
443         elog_event_t *eep;
444         u8 *s;
445
446         eep = get_clib_event (ep->datum);
447
448         s = format (0, "%U", format_elog_event, &elog_main, eep);
449         memcpy (tmpbuf, s, vec_len(s));
450         tmpbuf[vec_len(s)] = 0;
451         vec_free(s);
452         return;
453     }
454
455     snprintf(tmpbuf, len, "%s", edp->name);
456     fp = edp->format;
457     /* Make sure there's a real format string. If so, add it */
458     while (fp && *fp) {
459         if (*fp != ' ') {
460             snprintf(tmpbuf+strlen(tmpbuf), len - strlen(tmpbuf), ": ");
461             /* %s only supported for cpel files */
462             if (fp[1] == 's') {
463                 snprintf(tmpbuf+strlen(tmpbuf), len - strlen(tmpbuf),
464                          edp->format, strtab_ref(ep->datum));
465             } else {
466                 snprintf(tmpbuf+strlen(tmpbuf), len - strlen(tmpbuf),
467                         edp->format, ep->datum);
468             }
469             return;
470         }
471         fp++;
472     }
473 }
474
475 /****************************************************************************
476  * add_snapshot
477  ****************************************************************************/
478
479 static void add_snapshot(void)
480 {
481     int i;
482     snapshot_t *new = g_malloc(sizeof(snapshot_t));
483
484     memcpy(&new->geometry, s_v1, sizeof(new->geometry));
485     for (i = 0; i < NEVENTS; i++) {
486         new->show_event[i] = g_eventdefs[i].selected;
487     }
488     new->pidvec = g_malloc(sizeof(pid_sort_t)*g_npids);
489     memcpy(new->pidvec, g_pids, sizeof(pid_sort_t)*g_npids);
490     new->vscroll_value =  GTK_ADJUSTMENT(s_view1_vsadj)->value;
491     new->summary_mode = summary_mode;
492     new->color_mode = color_mode;
493
494     if (s_snapshots) {
495         new->next = s_snapshots;
496         s_snapshots = new;
497     } else {
498         new->next = 0;
499         s_snapshots = new;
500     }
501     s_cursnap = new;
502 }
503
504 /****************************************************************************
505  * next_snapshot
506  ****************************************************************************/
507
508 static void next_snapshot(void)
509 {
510     snapshot_t *next;
511     int i;
512     pid_sort_t *psp;
513     pid_data_t *pp;
514
515     if (!s_snapshots) {
516         infobox("No snapshots", "\nNo snapshots in the ring...\n");
517         return;
518     }
519
520     next = s_cursnap->next;
521     if (next == 0)
522         next = s_snapshots;
523
524     s_cursnap = next;
525
526     memcpy(s_v1, &next->geometry, sizeof(next->geometry));
527     for (i = 0; i < NEVENTS; i++) {
528         g_eventdefs[i].selected = next->show_event[i];
529     }
530     memcpy(g_pids, next->pidvec, sizeof(pid_sort_t)*g_npids);
531     color_mode = next->color_mode;
532     /*
533      * Update summary mode via a button push so that the button state is
534      * updated accordingly. (Should ideally clean up the view/controller
535      * separation properly one day.)
536      */
537     if (summary_mode != next->summary_mode) {
538         view1_button_click_callback
539             (NULL, (gpointer)(unsigned long long)
540              (summary_mode ? NOSUMMARY_BUTTON : SUMMARY_BUTTON));
541     }
542
543     /* Fix the pid structure index mappings */
544     psp = g_pids;
545
546     for (i = 0; i < g_npids; i++) {
547         pp = psp->pid;
548         pp->pid_index = i;
549         psp++;
550     }
551     GTK_ADJUSTMENT(s_view1_vsadj)->value = next->vscroll_value;
552     gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
553     recompute_hscrollbar();
554     pointsel_next_snapshot();
555     view1_display_when_idle();
556 }
557
558
559 /****************************************************************************
560  * del_snapshot
561  ****************************************************************************/
562
563 static void del_snapshot(void)
564 {
565     snapshot_t *prev;
566     snapshot_t *this;
567
568     if (!s_snapshots) {
569         infobox("No snapshots", "\nNo snapshots to delete...\n");
570         return;
571     }
572
573     prev = NULL;
574     this = s_snapshots;
575
576     while (this && this != s_cursnap) {
577         prev = this;
578         this = this->next;
579     }
580
581     if (this != s_cursnap) {
582         infobox("BUG", "\nSnapshot AWOL!\n");
583         return;
584     }
585
586     s_cursnap = this->next;
587
588     /* middle of the list? */
589     if (prev) {
590         prev->next = this->next;
591         g_free(this->pidvec);
592         g_free(this);
593     } else { /* start of the list */
594         s_snapshots = this->next;
595         g_free(this->pidvec);
596         g_free(this);
597     }
598
599     /* Note: both will be NULL after last delete */
600     if (s_cursnap == NULL)
601         s_cursnap = s_snapshots;
602 }
603
604 /****************************************************************************
605  * write_snapshot
606  *
607  * VERY primitive right now - not endian or version independent, and only
608  * writes to "snapshots.g2" in the current directory
609  ****************************************************************************/
610 static void write_snapshot(void)
611 {
612     FILE *file = NULL;
613     snapshot_t *snap;
614     char *error = NULL;
615     int records = 0;
616
617     if (s_snapshots == NULL) {
618         error = "No snapshots defined";
619         errno = 0;
620     }
621
622     if (!error) {
623         file = fopen("snapshots.g2", "w");
624         if (file == NULL) {
625             error = "Unable to open snapshots.g2";
626         }
627     }
628
629     /*
630      * Simply serialize the arch-dependent binary data, without a care in the
631      * world. Don't come running to me if you try to read it and crash.
632      */
633     for (snap = s_snapshots; !error && snap != NULL; snap = snap->next) {
634         if (fwrite(&snap->geometry,
635                    sizeof(snap->geometry), 1, file) != 1 ||
636             fwrite(&snap->show_event,
637                    sizeof(snap->show_event), 1, file) != 1 ||
638             fwrite(snap->pidvec,
639                    sizeof(pid_sort_t) * g_npids, 1, file) != 1 ||
640             fwrite(&snap->vscroll_value,
641                    sizeof(snap->vscroll_value), 1, file) != 1 ||
642             fwrite(&snap->summary_mode,
643                    sizeof(snap->summary_mode),   1, file) != 1 ||
644             fwrite(&snap->color_mode,
645                    sizeof(snap->color_mode),   1, file) != 1) {
646             error = "Error writing data";
647         }
648         records++;
649     }
650
651     if (!error) {
652         if (fclose(file)) {
653             error = "Unable to close file";
654         }
655     }
656
657     if (error) {
658         infobox(error, strerror(errno));
659     } else {
660         char buf[64];
661         snprintf(buf, sizeof(buf), "Wrote %d snapshots to snapshots.g2",
662                  records);
663         message_line(buf);
664     }
665 }
666
667 /****************************************************************************
668  * read_snapshot
669  *
670  * VERY primitive right now - not endian or version independent, and only reads
671  * from "snapshots.g2" in the current directory
672  ****************************************************************************/
673 static void read_snapshot(void)
674 {
675     FILE *file;
676     snapshot_t *snap, *next_snap;
677     snapshot_t *new_snaps = NULL;
678     char *error = NULL;
679     int len, i, records = 0;
680     pid_data_t *pp;
681
682     file = fopen("snapshots.g2", "r");
683     if (file == NULL) {
684         error = "Unable to open snapshots.g2";
685     }
686
687     /*
688      * Read in the snapshots and link them together. We insert them backwards,
689      * but that's tolerable. If the data is in anyway not what we expect, we'll
690      * probably crash. Sorry.
691      */
692     while (!error && !feof(file)) {
693         snap = g_malloc(sizeof(*snap));
694         snap->pidvec = NULL; /* so we can free this if there's an error */
695
696         len = fread(&snap->geometry, sizeof(snap->geometry), 1, file);
697         if (len == 0) {
698             /* EOF */
699             g_free(snap);
700             break;
701         } else {
702             /* insert into list straight away */
703             snap->next = new_snaps;
704             new_snaps = snap;
705         }
706         if (len != 1) {
707             error = "Problem reading first item from file";
708             break;
709         }
710         if (fread(&snap->show_event, sizeof(snap->show_event), 1, file) != 1) {
711             error = "Problem reading second item from file";
712             break;
713         }
714         len = sizeof(pid_sort_t) * g_npids;
715         snap->pidvec = g_malloc(len);
716         if (fread(snap->pidvec, len, 1, file) != 1) {
717             error = "Problem reading third item from file";
718             break;
719         }
720         if (fread(&snap->vscroll_value,
721                   sizeof(snap->vscroll_value), 1, file) != 1 ||
722             fread(&snap->summary_mode,
723                   sizeof(snap->summary_mode),  1, file) != 1 ||
724             fread(&snap->color_mode,
725                   sizeof(snap->color_mode),  1, file) != 1) {
726             error = "Problem reading final items from file";
727             break;
728         }
729
730         /*
731          * Fix up the pointers from the sorted pid vector back into our pid
732          * data objects, by walking the linked list of pid_data_t objects for
733          * every one looking for a match. This is O(n^2) grossness, but in real
734          * life there aren't that many pids, and it seems zippy enough.
735          */
736         for (i = 0; i < g_npids; i++) {
737             for (pp = g_pid_data_list; pp != NULL; pp = pp->next) {
738                 if (pp->pid_value == snap->pidvec[i].pid_value) {
739                     break;
740                 }
741             }
742             if (pp != NULL) {
743                 snap->pidvec[i].pid = pp;
744             } else {
745                 error = "Snapshot file referenced unknown pids";
746                 break;
747             }
748         }
749
750         records++;
751     }
752
753     if (!error) {
754         if (fclose(file)) {
755             error = "Unable to close file";
756         }
757     }
758
759     if (error) {
760         /*
761          * Problem - clear up any detritus
762          */
763         infobox(error, strerror(errno));
764         for (snap = new_snaps; snap != NULL; snap = next_snap) {
765             next_snap = snap->next;
766             g_free(snap);
767             g_free(snap->pidvec);
768         }
769     } else {
770         /*
771          * Success! trash the old snapshots and replace with the new
772          */
773         for (snap = s_snapshots; snap != NULL; snap = next_snap) {
774             next_snap = snap->next;
775             g_free(snap->pidvec);
776             g_free(snap);
777         }
778
779         s_cursnap = s_snapshots = new_snaps;
780     }
781
782     if (error) {
783         infobox(error, strerror(errno));
784     } else {
785         char buf[64];
786         snprintf(buf, sizeof(buf),
787                  "Read %d snapshots from snapshots.g2", records);
788         message_line(buf);
789     }
790 }
791
792 /****************************************************************************
793 * set_color
794 *
795 * Set the color for the specified pid_index, or COLOR_DEFAULT to return it
796 * to the usual black.
797 ****************************************************************************/
798 #define COLOR_DEFAULT (-1)
799 static void set_color(int pid_index)
800 {
801     pid_sort_t *psp;
802
803     psp = (g_pids + pid_index);
804
805     if (psp->selected)
806         gdk_gc_set_foreground(da->style->black_gc, &s_color[0]);
807     else if (pid_index == COLOR_DEFAULT || !color_mode) {
808         gdk_gc_set_foreground(da->style->black_gc, &fg_black);
809     } else {
810         gdk_gc_set_foreground(da->style->black_gc,
811                               &s_color[g_pids[pid_index].color_index]);
812     }
813 }
814
815 /****************************************************************************
816 * toggle_event_select
817 ****************************************************************************/
818
819 static int toggle_event_select(GdkEventButton *event, v1_geometry_t *vp)
820 {
821     int pid_index, start_index;
822     int x, y;
823     GdkRectangle *rp;
824     GdkRectangle hit_rect;
825     GdkRectangle dummy;
826     event_t *ep;
827     event_def_t *edp;
828     char tmpbuf [1024];
829     double time_per_pixel;
830
831     if (g_nevents == 0)
832         return 0;
833
834     time_per_pixel = dtime_per_pixel(vp);
835
836     start_index = find_event_index (vp->minvistime);
837
838     /* Too far right? */
839     if (start_index >= g_nevents)
840         return 0;
841
842     /*
843      * To see if the mouse hit a visible event, use a variant
844      * of the event display loop.
845      */
846
847     hit_rect.x = (int)event->x;
848     hit_rect.y = (int)event->y;
849     hit_rect.width = 1;
850     hit_rect.height = 1;
851
852     ep = (g_events + start_index);
853
854     while ((ep->time < vp->maxvistime) &&
855            (ep < (g_events + g_nevents))) {
856         pid_index = ep->pid->pid_index;
857
858         /* First filter: pid out of range */
859         if ((pid_index < vp->first_pid_index) ||
860             (pid_index >= vp->first_pid_index + vp->npids)) {
861             ep++;
862             continue;
863         }
864
865         /* Second filter: event hidden */
866         edp = find_event_definition (ep->code);
867         if (!edp->selected) {
868             ep++;
869             continue;
870         }
871
872         /*
873          * At this point, we know that the point is at least on the
874          * screen. See if the mouse hit within the bounding box
875          */
876
877         /*
878          * $$$$ maybe keep looping until off the edge,
879          * maintain a "best hit", then declare that one the winner?
880          */
881
882         pid_index -= vp->first_pid_index;
883
884         y = pid_index*vp->strip_height + vp->event_offset;
885
886         x = vp->pid_ax_width +
887             (int)(((double)(ep->time - vp->minvistime)) / time_per_pixel);
888
889         /* Perhaps we're trying to toggle the detail box? */
890         if (ep->flags & EVENT_FLAG_SELECT) {
891             /* Figure out the dimensions of the detail box */
892             format_popbox_string(tmpbuf, sizeof(tmpbuf), ep, edp);
893             rp = tbox(tmpbuf, x, y - vp->pop_offset, TBOX_GETRECT_BOXED);
894             if (gdk_rectangle_intersect(rp, &hit_rect, &dummy)) {
895                 ep->flags &= ~EVENT_FLAG_SELECT;
896                 view1_display_when_idle();
897                 return 0;
898             }
899         }
900
901         sprintf(tmpbuf, "%ld", ep->code);
902
903         /* Figure out the dimensions of the regular box */
904         rp = tbox(tmpbuf, x, y, TBOX_GETRECT_EVENT);
905
906         if (gdk_rectangle_intersect(rp, &hit_rect, &dummy)) {
907             /* we hit the rectangle. */
908             if (ep->flags & EVENT_FLAG_SELECT) {
909                 ep->flags &= ~EVENT_FLAG_SELECT;
910                 view1_display_when_idle();
911                 return 0;
912             } else {
913                 set_color(ep->pid->pid_index);
914
915                 /* It wasn't selected, so put up the detail box */
916                 format_popbox_string(tmpbuf, sizeof(tmpbuf), ep, edp);
917                 tbox(tmpbuf, x, y - vp->pop_offset, TBOX_DRAW_BOXED);
918                 line(x, y-vp->pop_offset, x, y, LINE_DRAW_BLACK);
919                 ep->flags |= EVENT_FLAG_SELECT;
920                 ep->flags &= ~EVENT_FLAG_SEARCHRSLT;
921                 s_last_selected_event = ep;
922             }
923             return 0;
924         }
925         ep++;
926     }
927     return -1;
928 }
929
930 /****************************************************************************
931 * toggle_track_select
932 ****************************************************************************/
933
934 static void toggle_track_select (GdkEventButton *event,
935                                  v1_geometry_t  *vp)
936 {
937     int i;
938     int pid_index;
939     int y, delta_y;
940     pid_sort_t *psp;
941
942     if (g_nevents == 0)
943         return;
944
945     /* Scan pid/track axis locations, looking for a match */
946     for (i = 0; i < vp->npids; i++) {
947         y = i*vp->strip_height + vp->pid_ax_offset;
948         delta_y = y - event->y;
949         if (delta_y < 0)
950             delta_y = -delta_y;
951         if (delta_y < 10) {
952             goto found;
953         }
954
955     }
956     infobox("NOTE", "\nNo PID/Track In Range\nPlease Try Again");
957     return;
958
959  found:
960     pid_index = i + vp->first_pid_index;
961     psp = (g_pids + pid_index);
962     psp->selected ^= 1;
963     view1_display_when_idle();
964 }
965
966 /****************************************************************************
967 * deselect_tracks
968 ****************************************************************************/
969 static void deselect_tracks (void)
970 {
971     int i;
972
973     for (i = 0; i < g_npids; i++)
974         g_pids[i].selected = 0;
975
976 }
977
978
979 /****************************************************************************
980 * move_current_track
981 ****************************************************************************/
982
983 typedef enum { MOVE_TOP, MOVE_BOTTOM } move_type;
984
985 static void move_current_track(GdkEventButton *event,
986                                v1_geometry_t  *vp,
987                                move_type       type)
988 {
989     int i;
990     int pid_index;
991     int y, delta_y;
992     pid_sort_t *new_pidvec;
993     pid_sort_t *psp;
994     pid_sort_t *pold, *pnew;
995     pid_data_t *pp;
996
997     if (g_nevents == 0)
998         return;
999
1000     /* Scan pid/track axis locations, looking for a match */
1001     for (i = 0; i < vp->npids; i++) {
1002         y = i*vp->strip_height + vp->pid_ax_offset;
1003         delta_y = y - event->y;
1004         if (delta_y < 0)
1005             delta_y = -delta_y;
1006         if (delta_y < 10) {
1007             goto found;
1008         }
1009
1010     }
1011     infobox("NOTE", "\nNo PID/Track In Range\nPlease Try Again");
1012     return;
1013
1014  found:
1015     pid_index = i + vp->first_pid_index;
1016
1017     new_pidvec = g_malloc0(sizeof(pid_sort_t)*g_npids);
1018     pold = g_pids;
1019     pnew = new_pidvec;
1020
1021     if (type == MOVE_TOP) {
1022         /* move to top */
1023         *pnew++ = g_pids[pid_index];
1024         for (i = 0; i < pid_index; i++)
1025             *pnew++ = *pold++;
1026         pold++;
1027         i++;
1028         for (; i < g_npids; i++)
1029             *pnew++ = *pold++;
1030     } else {
1031         /* move to bottom */
1032         for (i = 0; i < pid_index; i++)
1033             *pnew++ = *pold++;
1034         pold++;
1035         i++;
1036         for (; i < g_npids; i++)
1037             *pnew++ = *pold++;
1038         *pnew = g_pids[pid_index];
1039     }
1040
1041     g_free(g_pids);
1042     g_pids = new_pidvec;
1043
1044     /*
1045      * Revert the pid_index mapping to an identity map,
1046      */
1047     psp = g_pids;
1048
1049     for (i = 0; i < g_npids; i++) {
1050         pp = psp->pid;
1051         pp->pid_index = i;
1052         psp++;
1053     }
1054     view1_display_when_idle();
1055 }
1056
1057 /****************************************************************************
1058 * zoom_event
1059 * Process a zoom gesture. The use of doubles is required to avoid
1060 * truncating the various variable values, which in turn would lead to
1061 * some pretty random-looking zoom responses.
1062 ****************************************************************************/
1063
1064 void zoom_event(GdkEventButton *e1, GdkEventButton *e2, v1_geometry_t *vp)
1065 {
1066     double xrange;
1067     double time_per_pixel;
1068     double width_in_pixels;
1069     double center_on_time, width_in_time;
1070     double center_on_pixel;
1071
1072     /*
1073      * Clip the zoom area to the event display area.
1074      * Otherwise, center_on_time - width_in_time is in hyperspace
1075      * to the left of zero
1076      */
1077
1078     if (e1->x < vp->pid_ax_width)
1079         e1->x = vp->pid_ax_width;
1080
1081     if (e2->x < vp->pid_ax_width)
1082         e2->x = vp->pid_ax_width;
1083
1084     if (e2->x == e1->x)
1085         goto loser_zoom_repaint;
1086
1087     xrange = (double) (e2->x - e1->x);
1088     if (xrange < 0.00)
1089         xrange = -xrange;
1090
1091     /* Actually, width in pixels of half the zoom area */
1092     width_in_pixels = xrange / 2.00;
1093     time_per_pixel = dtime_per_pixel(vp);
1094     width_in_time = width_in_pixels * time_per_pixel;
1095
1096     /* Center the screen on the center of the zoom area */
1097     center_on_pixel = (double)((e2->x + e1->x) / 2.00) -
1098         (double)vp->pid_ax_width;
1099     center_on_time = center_on_pixel*time_per_pixel + (double)vp->minvistime;
1100
1101     /*
1102      * Transform back to 64-bit integer microseconds, reset the
1103      * scrollbar, schedule a repaint.
1104      */
1105     vp->minvistime = (ulonglong)(center_on_time - width_in_time);
1106     vp->maxvistime = (ulonglong)(center_on_time + width_in_time);
1107
1108 loser_zoom_repaint:
1109     recompute_hscrollbar();
1110
1111     view1_display_when_idle();
1112 }
1113
1114 /****************************************************************************
1115 * scroll_y
1116 *
1117 * Scroll up or down by the specified delta
1118 *
1119 ****************************************************************************/
1120 static void scroll_y(int delta)
1121 {
1122     int new_index = s_v1->first_pid_index + delta;
1123     if (new_index + s_v1->npids > g_npids)
1124         new_index = g_npids - s_v1->npids;
1125     if (new_index < 0)
1126         new_index = 0;
1127
1128     if (new_index != s_v1->first_pid_index) {
1129         s_v1->first_pid_index = new_index;
1130         GTK_ADJUSTMENT(s_view1_vsadj)->value = (gdouble)new_index;
1131         gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
1132         view1_display_when_idle();
1133     }
1134 }
1135
1136 /****************************************************************************
1137 * view1_handle_key_press_event
1138 * Relevant definitions in: /usr/include/gtk-1.2/gdk/gdktypes.h
1139 *
1140 * This routine implements hotkeys for the Quake generation:
1141 *
1142 *   W - zoom in
1143 *   S - zoom out
1144 *   A - pan left
1145 *   D - pan right
1146 *   R - pan up
1147 *   F - pan down
1148 *   T - more traces
1149 *   G - fewer traces
1150 *
1151 *   E - toggle summary mode
1152 *   C - toggle color mode
1153 *
1154 *   X - take snapshot
1155 *   Z - next snapshot
1156 *   P - persist snapshots to file
1157 *   L - load snapshots from file
1158 *
1159 * ctrl-Q - exit
1160 *
1161 ****************************************************************************/
1162 gint
1163 view1_handle_key_press_event (GtkWidget *widget, GdkEventKey *event)
1164 {
1165     long long delta;
1166
1167     switch (event->keyval) {
1168         case GDK_w: // zoom in
1169             view1_button_click_callback(NULL, (gpointer)ZOOMIN_BUTTON);
1170             break;
1171
1172         case GDK_s: // zoom out
1173             view1_button_click_callback(NULL, (gpointer)ZOOMOUT_BUTTON);
1174             break;
1175
1176         case GDK_a: // pan left
1177             delta = (s_v1->maxvistime - s_v1->minvistime) / 6;
1178             if (s_v1->minvistime < delta) {
1179                 delta = s_v1->minvistime;
1180             }
1181             s_v1->minvistime -= delta;
1182             s_v1->maxvistime -= delta;
1183             recompute_hscrollbar();
1184             break;
1185
1186         case GDK_d: // pan right
1187             delta = (s_v1->maxvistime - s_v1->minvistime) / 6;
1188             if (s_v1->maxvistime + delta > g_events[g_nevents - 1].time) {
1189                 /*
1190                  * @@@ this doesn't seem to quite reach the far right hand
1191                  * side correctly - not sure why.
1192                  */
1193                 delta = g_events[g_nevents - 1].time - s_v1->maxvistime;
1194             }
1195             s_v1->minvistime += delta;
1196             s_v1->maxvistime += delta;
1197             recompute_hscrollbar();
1198             break;
1199
1200         case GDK_r: // pan up
1201             scroll_y(-1);
1202             break;
1203
1204         case GDK_f: // pan down
1205             scroll_y(+1);
1206             break;
1207
1208         case GDK_t: // fewer tracks
1209             view1_button_click_callback(NULL, (gpointer)LESS_TRACES_BUTTON);
1210             break;
1211
1212         case GDK_g: // more tracks
1213             view1_button_click_callback(NULL, (gpointer)MORE_TRACES_BUTTON);
1214             break;
1215
1216         case GDK_e: // toggle summary mode
1217             view1_button_click_callback
1218                 (NULL, (gpointer)(unsigned long long)
1219                  (summary_mode ? NOSUMMARY_BUTTON : SUMMARY_BUTTON));
1220             break;
1221
1222         case GDK_c: // toggle color mode
1223             color_mode ^= 1;
1224             view1_display_when_idle();
1225             break;
1226
1227         case GDK_p: // persist snapshots
1228             write_snapshot();
1229             break;
1230
1231         case GDK_l: // load snapshots
1232             read_snapshot();
1233             break;
1234
1235         case GDK_x: // take snapshot
1236             view1_button_click_callback(NULL, (gpointer)SNAP_BUTTON);
1237             break;
1238
1239         case GDK_z: // next snapshot
1240             view1_button_click_callback(NULL, (gpointer)NEXT_BUTTON);
1241             break;
1242
1243         case GDK_q: // ctrl-q is exit
1244             if (event->state & GDK_CONTROL_MASK) {
1245                 gtk_main_quit();
1246             }
1247             break;
1248     }
1249     return TRUE;
1250 }
1251
1252 /****************************************************************************
1253 * button_press_event
1254 * Relevant definitions in: /usr/include/gtk-1.2/gdk/gdktypes.h
1255 *
1256 * This routine implements three functions: zoom-to-area, time ruler, and
1257 * show/hide event detail popup.
1258 *
1259 * The left mouse button (button 1) has two simultaneous functions: event
1260 * detail popup, and zoom-to-area. If the press and release events occur
1261 * within a small delta-x, it's a detail popup event.  Otherwise, it's
1262 * an area zoom.
1263 *
1264 * The right mouse button (button 3) implements the time ruler.
1265 ****************************************************************************/
1266
1267 static gint
1268 button_press_event (GtkWidget *widget, GdkEventButton *event)
1269 {
1270     static GdkEventButton press1_event;
1271     static boolean press1_valid;
1272     static GdkEventButton press3_event;
1273     static guint32 last_truler_time;
1274     static boolean press3_valid;
1275     static boolean zoom_bar_up;
1276     int time_ax_y, xdelta;
1277     char tmpbuf [128];
1278     double time_per_pixel;
1279
1280     time_ax_y = 0;
1281
1282     switch(event->type) {
1283     case GDK_BUTTON_PRESS:
1284         /* Capture the appropriate starting point */
1285         if (event->button == 1) {
1286             press1_valid = TRUE;
1287             press1_event = *event;
1288             return(TRUE);
1289         }
1290         if (event->button == 3) {
1291             press3_valid = TRUE;
1292             press3_event = *event;
1293             return(TRUE);
1294         }
1295         return(TRUE);
1296
1297     case GDK_BUTTON_RELEASE:
1298         /* Time ruler */
1299         if (press3_valid) {
1300             press3_valid = FALSE;
1301             /* Fix the cursor, and repaint the screen from scratch */
1302             gdk_window_set_cursor (da->window, norm_cursor);
1303             view1_display_when_idle();
1304             return(TRUE);
1305         }
1306         /* Event select / zoom-to-area */
1307         if (press1_valid) {
1308             press1_valid = FALSE;
1309             xdelta = (int)(press1_event.x - event->x);
1310             if (xdelta < 0)
1311                 xdelta = -xdelta;
1312
1313             /* is the mouse more or less where it started? */
1314             if (xdelta < 10) {
1315                 /* Control-left-mouse => sink the track */
1316                 /* Shift-left-mouse => raise the track */
1317                 if ((press1_event.state & GDK_CONTROL_MASK) ==
1318                     GDK_CONTROL_MASK) {
1319                     move_current_track(event, s_v1, MOVE_BOTTOM);
1320                 } else if ((press1_event.state & GDK_SHIFT_MASK) ==
1321                            GDK_SHIFT_MASK) {
1322                     move_current_track(event, s_v1, MOVE_TOP);
1323                 } else {
1324                     /* No modifiers: toggle the event / select track */
1325                     if (toggle_event_select(event, s_v1))
1326                         toggle_track_select(event, s_v1);
1327                 }
1328                 /* Repaint to get rid of the zoom bar */
1329                 if (zoom_bar_up) {
1330                     /* Fix the cursor and leave. No zoom */
1331                     gdk_window_set_cursor (da->window, norm_cursor);
1332                     zoom_bar_up = FALSE;
1333                     break;
1334                 }
1335             } else { /* mouse moved enough to zoom */
1336                 zoom_event(&press1_event, event, s_v1);
1337                 gdk_window_set_cursor (da->window, norm_cursor);
1338                 zoom_bar_up = FALSE;
1339             }
1340         }  else if (event->button == 4) {
1341             /* scroll wheel up */
1342             scroll_y(event->state & GDK_SHIFT_MASK ? -10 : -1);
1343         } else if (event->button == 5) {
1344             /* scroll wheel down */
1345             scroll_y(event->state & GDK_SHIFT_MASK ? +10 : +1);
1346         }
1347         return(TRUE);
1348
1349     case GDK_MOTION_NOTIFY:
1350         /* Button one followed by motion: draw zoom fence and fix cursor */
1351         if (press1_valid) {
1352             /* Fence, cursor already set */
1353             if (zoom_bar_up)
1354                 return(TRUE);
1355
1356             xdelta = (int)(press1_event.x - event->x);
1357             if (xdelta < 0)
1358                 xdelta = -xdelta;
1359
1360             /* Haven't moved enough to declare a zoom sequence yet */
1361             if (xdelta < 10)
1362                 return(TRUE);
1363
1364             /* Draw the zoom fence, use the key-down X coordinate */
1365             time_ax_y = s_v1->npids * s_v1->strip_height + s_v1->pid_ax_offset;
1366
1367             line((int)(press1_event.x), s_v1->pop_offset,
1368                  (int)(press1_event.x), time_ax_y, LINE_DRAW_BLACK);
1369             tbox("Zoom From Here...", (int)(press1_event.x), s_v1->pop_offset,
1370                  TBOX_DRAW_BOXED);
1371             gdk_window_set_cursor(da->window, zi_cursor);
1372             zoom_bar_up = TRUE;
1373             return(TRUE);
1374         }
1375         if (press3_valid) {
1376             double nsec;
1377
1378             gdk_window_set_cursor(da->window, zi_cursor);
1379
1380             /*
1381              * Some filtration is needed on Solaris, or the server will hang
1382              */
1383             if (event->time - last_truler_time < 75)
1384                 return(TRUE);
1385
1386             last_truler_time = event->time;
1387
1388             line((int)(press3_event.x), s_v1->pop_offset,
1389                  (int)(press3_event.x), time_ax_y, LINE_DRAW_BLACK);
1390
1391             xdelta = (int)(press3_event.x - event->x);
1392             if (xdelta < 0)
1393                 xdelta = -xdelta;
1394
1395             time_per_pixel = ((double)(s_v1->maxvistime - s_v1->minvistime)) /
1396                 ((double)(s_v1->total_width - s_v1->pid_ax_width));
1397
1398             time_ax_y = s_v1->npids * s_v1->strip_height + s_v1->pid_ax_offset;
1399
1400             line((int)(press3_event.x), s_v1->pop_offset,
1401                  (int)(press3_event.x), time_ax_y, LINE_DRAW_BLACK);
1402             /*
1403              * Note: use a fixed-width format so it looks like we're
1404              * erasing and redrawing the box.
1405              */
1406             nsec = ((double)xdelta)*time_per_pixel;
1407             if (nsec >1e9) {
1408                 sprintf(tmpbuf, "%8.3f sec ", nsec/1e9);
1409             } else if (nsec > 1e6) {
1410                 sprintf(tmpbuf, "%8.3f msec", nsec/1e6);
1411             } else if (nsec > 1e3) {
1412                 sprintf(tmpbuf, "%8.3f usec", nsec/1e3);
1413             } else {
1414                 sprintf(tmpbuf, "%8.0f nsec", nsec);
1415             }
1416             s_v1->last_time_interval = nsec;
1417             tbox(tmpbuf, (int)(press3_event.x), s_v1->pop_offset,
1418                  TBOX_DRAW_BOXED);
1419             return(TRUE);
1420         }
1421
1422     default:
1423         break;
1424 #ifdef DEBUG
1425         g_print("button:\ttype = %d\n", event->type);
1426         g_print("\twindow = 0x%x\n", event->window);
1427         g_print("\tsend_event = %d\n", event->send_event);
1428         g_print("\ttime = %d\n", event->time);
1429         g_print("\tx = %6.2f\n", event->x);
1430         g_print("\ty = %6.2f\n", event->y);
1431         g_print("\tpressure = %6.2f\n", event->pressure);
1432         g_print("\txtilt = %6.2f\n", event->xtilt);
1433         g_print("\tytilt = %6.2f\n", event->ytilt);
1434         g_print("\tstate = %d\n", event->state);
1435         g_print("\tbutton = %d\n", event->button);
1436         g_print("\tsource = %d\n", event->source);
1437         g_print("\tdeviceid = %d\n", event->deviceid);
1438         g_print("\tx_root = %6.2f\n", event->x_root);
1439         g_print("\ty_root = %6.2f\n", event->y_root);
1440         return(TRUE);
1441 #endif
1442     }
1443
1444     view1_display_when_idle();
1445
1446     return(TRUE);
1447 }
1448
1449 /****************************************************************************
1450 * configure_event
1451 * Happens when the window manager resizes the viewer's main window.
1452 ****************************************************************************/
1453
1454 static gint
1455 configure_event (GtkWidget *widget, GdkEventConfigure *event)
1456 {
1457     /* Toss the previous drawing area backing store pixmap */
1458     if (pm)
1459         gdk_pixmap_unref(pm);
1460
1461     /* Create a new pixmap, paint it */
1462     pm = gdk_pixmap_new(widget->window,
1463                         widget->allocation.width,
1464                         widget->allocation.height,
1465                         -1);
1466     gdk_draw_rectangle (pm,
1467                         widget->style->white_gc,
1468                         TRUE,
1469                         0, 0,
1470                         widget->allocation.width,
1471                         widget->allocation.height);
1472
1473     /* Reset the view geometry parameters, as required */
1474     s_v1->total_width = widget->allocation.width;
1475     s_v1->total_height = widget->allocation.height;
1476     s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) /
1477         s_v1->strip_height;
1478
1479     /* Schedule a repaint */
1480     view1_display_when_idle();
1481     return(TRUE);
1482 }
1483
1484 /****************************************************************************
1485 * expose_event
1486 * Use backing store to fix the screen.
1487 ****************************************************************************/
1488 static gint expose_event (GtkWidget *widget, GdkEventExpose *event)
1489 {
1490     gdk_draw_pixmap(widget->window,
1491                     widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
1492                     pm,
1493                     event->area.x, event->area.y,
1494                     event->area.x, event->area.y,
1495                     event->area.width, event->area.height);
1496
1497     return(FALSE);
1498 }
1499
1500 /****************************************************************************
1501 * event_search_internal
1502 * This routine searches forward from s_srchindex, looking for s_srchcode;
1503 * wraps at the end of the buffer.
1504 ****************************************************************************/
1505
1506 boolean event_search_internal (void)
1507 {
1508     event_t *ep;
1509     int i;
1510     int index;
1511     int pid_index;
1512     boolean full_redisplay = FALSE;
1513     ulonglong current_width;
1514     char tmpbuf [64];
1515
1516     /* No events yet?  Act like the search worked, to avoid a loop */
1517     if (g_nevents == 0)
1518         return(TRUE);
1519
1520     ep = (g_events + s_srchindex);
1521     ep->flags &= ~EVENT_FLAG_SEARCHRSLT;
1522
1523     /*
1524      * Assume the user wants to search [plus or minus]
1525      * from where they are.
1526      */
1527 #ifdef notdef
1528     if (ep->time < s_v1->minvistime)
1529         s_srchindex = find_event_index (s_v1->minvistime);
1530 #endif
1531
1532     for (i = 1; i <= g_nevents; i++) {
1533         index = (srch_chase_dir == SRCH_CHASE_BACKWARD) ?
1534             (s_srchindex - i) % g_nevents :
1535             (i + s_srchindex) % g_nevents;
1536
1537         ep = (g_events + index);
1538
1539         if (ep->code == s_srchcode) {
1540             if (s_srchfail_up)
1541                 message_line("");
1542             s_srchindex = index;
1543             pid_index = ep->pid->pid_index;
1544
1545             /* Need a vertical scroll? */
1546             if ((pid_index < s_v1->first_pid_index) ||
1547                 (pid_index >= s_v1->first_pid_index + s_v1->npids)) {
1548                 if (pid_index > (g_npids - s_v1->npids))
1549                     pid_index = (g_npids - s_v1->npids);
1550                 s_v1->first_pid_index = pid_index;
1551                 GTK_ADJUSTMENT(s_view1_vsadj)->value =
1552                     (gdouble)s_v1->first_pid_index;
1553                 gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
1554                 full_redisplay = TRUE;
1555             }
1556
1557             /* Need a horizontal scroll? */
1558             if (ep->time < s_v1->minvistime || ep->time > s_v1->maxvistime) {
1559                 current_width = (s_v1->maxvistime - s_v1->minvistime);
1560                 if (ep->time < ((current_width+1) / 2)) {
1561                     s_v1->minvistime = 0ll;
1562                     s_v1->maxvistime = current_width;
1563                 } else {
1564                     s_v1->minvistime = ep->time - ((current_width+1)/2);
1565                     s_v1->maxvistime = ep->time + ((current_width+1)/2);
1566                 }
1567                 recompute_hscrollbar();
1568                 full_redisplay = TRUE;
1569             }
1570             ep->flags |= EVENT_FLAG_SEARCHRSLT;
1571             full_redisplay = TRUE;
1572
1573 #ifdef NOTDEF
1574             if (!full_redisplay){
1575                 if (!s_result_up) {
1576                     s_result_up = TRUE;
1577                     time_per_pixel = dtime_per_pixel(s_v1);
1578
1579                     y = pid_index*s_v1->strip_height + s_v1->event_offset;
1580                     x = s_v1->pid_ax_width +
1581                         (int)(((double)(ep->time - s_v1->minvistime)) /
1582                               time_per_pixel);
1583                     sprintf(tmpbuf, "SEARCH RESULT");
1584                     tbox(tmpbuf, x, y - s_v1->pop_offset, TBOX_DRAW_BOXED);
1585                     line(x, y-s_v1->pop_offset, x, y, LINE_DRAW_BLACK);
1586                 } else {
1587                     full_redisplay = TRUE;
1588                 }
1589             }
1590 #endif
1591
1592             if (full_redisplay)
1593                 view1_display_when_idle();
1594             return(TRUE);
1595         }
1596     }
1597     sprintf (tmpbuf, "Search for event %ld failed...\n", s_srchcode);
1598     message_line(tmpbuf);
1599     s_srchfail_up = TRUE;
1600     return(TRUE);
1601 }
1602
1603 /****************************************************************************
1604 * event_search_callback
1605 ****************************************************************************/
1606
1607 boolean event_search_callback (char *s)
1608 {
1609     /* No events yet?  Act like the search worked, to avoid a loop */
1610     if (g_nevents == 0)
1611         return(TRUE);
1612
1613     s_srchcode = atol(s);
1614
1615     if (s_srchcode == 0)
1616         return(FALSE);
1617
1618     return(event_search_internal());
1619 }
1620
1621
1622 /****************************************************************************
1623 * anomaly_statistics_init
1624 ****************************************************************************/
1625
1626 static int anomaly_statistics_init (void)
1627 {
1628     elog_event_t *eep;
1629     u32 data;
1630     event_t *ep;
1631     pid_data_t *pid;
1632     int i;
1633     int index;
1634     int pid_index;
1635     f64 fdata;
1636
1637     /* Gather summary statistics... */
1638     ep = g_events;
1639
1640     vec_reset_length (s_v1->means);
1641     vec_reset_length (s_v1->matches);
1642     vec_reset_length (s_v1->variances);
1643     vec_reset_length (s_v1->two_stddevs);
1644     vec_reset_length (s_v1->mins);
1645     vec_reset_length (s_v1->maxes);
1646
1647     for (i = 0; i < g_nevents; i++) {
1648         if (ep->code != s_anomalycode) {
1649             ep++;
1650             continue;
1651         }
1652         pid = ep->pid;
1653         vec_validate_init_empty (s_v1->matches, pid->pid_index, 0);
1654         vec_validate_init_empty (s_v1->means, pid->pid_index, 0.0);
1655         vec_validate_init_empty (s_v1->mins, pid->pid_index, 0.0);
1656         vec_validate_init_empty (s_v1->maxes, pid->pid_index, 0.0);
1657         eep = get_clib_event (ep->datum);
1658         data = clib_mem_unaligned (eep->data, u32);
1659         fdata = data;
1660         s_v1->means[pid->pid_index] += fdata;
1661         s_v1->matches[pid->pid_index] += 1;
1662         /* First data point? set min, max */
1663         if (PREDICT_FALSE(s_v1->matches[pid->pid_index] == 1)) {
1664             s_v1->mins[pid->pid_index] = fdata;
1665             s_v1->maxes[pid->pid_index] = fdata;
1666         } else {
1667             s_v1->mins[pid->pid_index] = (fdata < s_v1->mins[pid->pid_index]) ?
1668                 fdata : s_v1->mins[pid->pid_index];
1669             s_v1->maxes[pid->pid_index] =
1670                 (fdata > s_v1->maxes[pid->pid_index]) ?
1671                 fdata : s_v1->maxes[pid->pid_index];
1672         }
1673         ep++;
1674     }
1675     if (vec_len (s_v1->matches) == 0)
1676         return -1;
1677
1678     /* Compute s_v1->means */
1679     for (i = 0; i < vec_len (s_v1->means); i++)
1680         s_v1->means[i] = s_v1->matches[i]
1681             ? (s_v1->means[i] / (f64) s_v1->matches[i]) : 0.0;
1682
1683     /* Compute s_v1->variances */
1684     ep = g_events;
1685     for (i = 0; i < g_nevents; i++) {
1686         if (ep->code != s_anomalycode) {
1687             ep++;
1688             continue;
1689         }
1690         pid = ep->pid;
1691         vec_validate_init_empty (s_v1->variances, pid->pid_index, 0);
1692         eep = get_clib_event (ep->datum);
1693         data = clib_mem_unaligned (eep->data, u32);
1694         fdata = data;
1695         s_v1->variances[pid->pid_index] +=
1696             (fdata - s_v1->means[pid->pid_index])
1697             * (fdata - s_v1->means[pid->pid_index]);
1698         ep++;
1699     }
1700
1701     /* Normalize variances */
1702     for (i = 0; i < vec_len (s_v1->variances); i++)
1703         s_v1->variances[i] = s_v1->matches[i]
1704             ? (s_v1->variances[i] / (f64) s_v1->matches[i]) : 0.0;
1705
1706     /* Compute the anomaly threshold, by default 2.5*stddev */
1707     for (i = 0; i < vec_len (s_v1->variances); i++)
1708         vec_add1 (s_v1->two_stddevs,
1709                   s_v1->anomaly_threshold_stddevs * sqrt(s_v1->variances[i]));
1710     return 0;
1711 }
1712
1713 /****************************************************************************
1714 * anomaly_search_internal
1715 * This routine searches forward from s_srchindex, looking for s_srchcode;
1716 * wraps at the end of the buffer.
1717 ****************************************************************************/
1718
1719 boolean anomaly_search_internal (void)
1720 {
1721     elog_event_t *eep;
1722     u32 data;
1723     event_t *ep;
1724     pid_data_t *pid;
1725     int i;
1726     int index;
1727     int pid_index;
1728     boolean full_redisplay = FALSE;
1729     ulonglong current_width;
1730     char tmpbuf [64];
1731     f64 fdata;
1732
1733     if (vec_len (s_v1->matches) == 0)
1734         anomaly_statistics_init();
1735
1736     ep = (g_events + s_srchindex);
1737     ep->flags &= ~EVENT_FLAG_SEARCHRSLT;
1738
1739     /*
1740      * If the user rearranged the screen, start from the minimum
1741      * visible time
1742      */
1743     if (ep->time < s_v1->minvistime)
1744         s_srchindex = find_event_index (s_v1->minvistime);
1745
1746     for (i = 1; i <= g_nevents; i++) {
1747         index = (i + s_srchindex) % g_nevents;
1748
1749         ep = (g_events + index);
1750         if (ep->code != s_anomalycode)
1751             continue;
1752         pid = ep->pid;
1753
1754         eep = get_clib_event (ep->datum);
1755         data = clib_mem_unaligned (eep->data, u32);
1756         fdata = data;
1757
1758         /*
1759          * Found an anomaly? Define an anomaly as a datum
1760          * greater than 2*stddev above average.
1761          */
1762         if ((fdata - s_v1->means[pid->pid_index]) >
1763             s_v1->two_stddevs[pid->pid_index]) {
1764             u8 *s;
1765
1766             s = format (0, "%.1f*stddev {min,max,mean,threshold}: ",
1767                         s_v1->anomaly_threshold_stddevs);
1768
1769             for (i = 0; i < vec_len (s_v1->means); i++) {
1770                 if (s_v1->matches[i] > 0)
1771                     s = format (s, "{%.0f, %.0f, %.0f, %.0f} ",
1772                                 s_v1->mins[i], s_v1->maxes[i],
1773                                 s_v1->means[i],
1774                                 s_v1->means[i]+s_v1->two_stddevs[i]);
1775                 else
1776                     s = format (s, "{no match} ");
1777             }
1778
1779             message_line ((char *)s);
1780             vec_free (s);
1781
1782             s_srchindex = index;
1783             pid_index = ep->pid->pid_index;
1784
1785             /* Need a vertical scroll? */
1786             if ((pid_index < s_v1->first_pid_index) ||
1787                 (pid_index >= s_v1->first_pid_index + s_v1->npids)) {
1788                 if (pid_index > (g_npids - s_v1->npids))
1789                     pid_index = (g_npids - s_v1->npids);
1790                 s_v1->first_pid_index = pid_index;
1791                 GTK_ADJUSTMENT(s_view1_vsadj)->value =
1792                     (gdouble)s_v1->first_pid_index;
1793                 gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
1794                 full_redisplay = TRUE;
1795             }
1796
1797             /* Need a horizontal scroll? */
1798             if (ep->time < s_v1->minvistime || ep->time > s_v1->maxvistime) {
1799                 current_width = (s_v1->maxvistime - s_v1->minvistime);
1800                 if (ep->time < ((current_width+1) / 2)) {
1801                     s_v1->minvistime = 0ll;
1802                     s_v1->maxvistime = current_width;
1803                 } else {
1804                     s_v1->minvistime = ep->time - ((current_width+1)/2);
1805                     s_v1->maxvistime = ep->time + ((current_width+1)/2);
1806                 }
1807                 recompute_hscrollbar();
1808                 full_redisplay = TRUE;
1809             }
1810             ep->flags |= EVENT_FLAG_SEARCHRSLT;
1811             full_redisplay = TRUE;
1812
1813             if (full_redisplay)
1814                 view1_display_when_idle();
1815
1816             return(TRUE);
1817         }
1818     }
1819     sprintf (tmpbuf, "Search for an anomalous event %ld failed...\n",
1820              s_anomalycode);
1821     message_line(tmpbuf);
1822     s_srchfail_up = TRUE;
1823     return(TRUE);
1824 }
1825
1826 /****************************************************************************
1827 * anomaly_search_callback
1828 ****************************************************************************/
1829
1830 boolean anomaly_search_callback (char *s)
1831 {
1832     ulong new_anomalycode;
1833
1834     /* No events yet?  Act like the search worked, to avoid a loop */
1835     if (g_nevents == 0)
1836         return(TRUE);
1837
1838     new_anomalycode = atol(s);
1839
1840     if (new_anomalycode == 0)
1841         return(FALSE);
1842
1843     if (new_anomalycode != s_anomalycode ||
1844         vec_len (s_v1->matches) == 0) {
1845         s_anomalycode = new_anomalycode;
1846         if (anomaly_statistics_init()) {
1847             u8 *s;
1848
1849             s = format (0, "Search for an anomalous event %ld failed...\n",
1850                         s_anomalycode);
1851             message_line ((char *) s);
1852             vec_free (s);
1853             return (TRUE);
1854         }
1855     }
1856     return(anomaly_search_internal());
1857 }
1858
1859 /****************************************************************************
1860 * anomaly_threshold_callback
1861 ****************************************************************************/
1862
1863 boolean anomaly_threshold_callback (char *s)
1864 {
1865     f64 new_threshold;
1866
1867     /* No events yet?  Act like the search worked, to avoid a loop */
1868     if (g_nevents == 0)
1869         return(TRUE);
1870
1871     new_threshold = atof (s);
1872
1873     if (new_threshold == 0.0 || new_threshold > 10.0)
1874         return(FALSE);
1875
1876     s_v1->anomaly_threshold_stddevs = new_threshold;
1877
1878     vec_reset_length (s_v1->means);
1879     vec_reset_length (s_v1->matches);
1880     vec_reset_length (s_v1->variances);
1881     vec_reset_length (s_v1->two_stddevs);
1882     return (TRUE);
1883 }
1884
1885 /****************************************************************************
1886 * event_search
1887 ****************************************************************************/
1888
1889 static void event_search (void)
1890 {
1891     modal_dialog ("Event Search: Please Enter Event Code",
1892                   "Invalid: Please Reenter Event Code", NULL,
1893                   event_search_callback);
1894 }
1895
1896 /****************************************************************************
1897 * anomaly_search
1898 ****************************************************************************/
1899
1900 static void anomaly_search (void)
1901 {
1902     modal_dialog ("Anomaly Search: Please Enter Event Code",
1903                   "Invalid: Please Reenter Event Code", NULL,
1904                   anomaly_search_callback);
1905 }
1906
1907 /****************************************************************************
1908 * anomaly_threshold
1909 ****************************************************************************/
1910
1911 static void anomaly_threshold (void)
1912 {
1913     modal_dialog ("Anomaly Threshold: Please Enter Threshold",
1914                   "Invalid: Please Reenter Threshold in Standard Deviations",
1915                   NULL, anomaly_threshold_callback);
1916 }
1917
1918 /****************************************************************************
1919 * init_track_colors
1920 ****************************************************************************/
1921 static void init_track_colors(void)
1922 {
1923     int         i;
1924     unsigned    hash;
1925     char       *label_char;
1926     unsigned    RGB[3];
1927     gboolean    dont_care[g_npids];
1928
1929     /*
1930      * If we've already allocated the colors once, then in theory we should
1931      * just be able to re-order the GCs already created to match the new track
1932      * order; the track -> color mapping doesn't currently change at runtime.
1933      * However, it's easier just to allocate everything from fresh. As a nod in
1934      * the direction of politeness towards our poor abused X server, we at
1935      * least mop up the previously allocated GCs first, although in practice
1936      * even omitting this didn't seem to cause a problem.
1937      */
1938     if (s_color != NULL ) {
1939         gdk_colormap_free_colors(gtk_widget_get_colormap(da),
1940                                  s_color, g_npids);
1941         clib_memset(s_color, 0, sizeof(GdkColor) * g_npids);
1942     } else {
1943         /*
1944          * First time through: allocate the array to hold the GCs.
1945          */
1946         s_color = g_malloc(sizeof(GdkColor) * (g_npids+1));
1947     }
1948
1949     /*
1950      * Go through and assign a color for each track.
1951      */
1952     /* Setup entry 0 in the colormap as pure red (for selection) */
1953     s_color[0] = fg_red;
1954
1955     for (i = 1; i < g_npids; i++) {
1956         /*
1957          * We compute the color from a hash of the thread name. That way we get
1958          * a distribution of different colors, and the same thread has the same
1959          * color across multiple data sets. Unfortunately, even though the
1960          * process name and thread id are invariant across data sets, the
1961          * process id isn't, so we want to exclude that from the hash. Since
1962          * the pid appears in parentheses after the process name and tid, we
1963          * can just stop at the '(' character.
1964          *
1965          * We could create a substring and use the CLIB Jenkins hash, but given
1966          * we're hashing ascii data, a suitable Bernstein hash is pretty much
1967          * just as good, and it's easiest just to compute it inline.
1968          */
1969         label_char = get_track_label(g_pids[i].pid_value);
1970         hash = 0;
1971         while (*label_char != '\0' && *label_char != '(') {
1972             hash = hash * 33 + *label_char++;
1973         }
1974         hash += hash >> 5;    /* even out the lower order bits a touch */
1975
1976         /*
1977          * OK, now we have our hash. We get the color by using the first three
1978          * bytes of the hash for the RGB values (expanded from 8 to 16 bits),
1979          * and then use the fourth byte to choose one of R, G, B and mask this
1980          * one down. This ensures the color can't be too close to white and
1981          * therefore hard to see.
1982          *
1983          * We also drop the top bit of the green, since bright green on its own
1984          * is hard to see against white. Generally we err on the side of
1985          * keeping it dark, rather than using the full spectrum of colors. This
1986          * does result in something of a preponderance of muddy colors and a
1987          * bit of a lack of cheery bright ones, but at least you can read
1988          * everything. It would be nice to do better.
1989          */
1990         RGB[0] = (hash & 0xff000000) >> 16;
1991         RGB[1] = (hash & 0x007f0000) >> 8;
1992         RGB[2] = (hash & 0x0000ff00);
1993         RGB[hash % 3] &= 0x1fff;
1994
1995         {
1996             GdkColor color = {0, RGB[0], RGB[1], RGB[2]};
1997             s_color[i] = color;
1998             g_pids[i].color_index = i;
1999         }
2000     }
2001
2002     /*
2003      * Actually allocate the colors in one bulk operation. We ignore the return
2004      * values.
2005      */
2006     gdk_colormap_alloc_colors(gtk_widget_get_colormap(da),
2007                               s_color, g_npids+1, FALSE, TRUE, dont_care);
2008 }
2009
2010
2011 /****************************************************************************
2012 * chase_event_etc
2013 * Reorder the pid_index fields so the viewer "chases" the last selected
2014 * event.
2015 ****************************************************************************/
2016
2017 static void chase_event_etc(enum chase_mode mode)
2018 {
2019     pid_sort_t *psp, *new_pidvec;
2020     pid_data_t *pp;
2021     event_t *ep;
2022     int pids_mapped;
2023     ulong code_to_chase;
2024     ulong datum_to_chase;
2025     ulong pid_to_chase;
2026     int i;
2027     int winner;
2028
2029     if (!s_last_selected_event) {
2030         infobox("No selected event",
2031                 "\nPlease select an event and try again...\n");
2032         return;
2033     }
2034
2035     /* Clear all index assignments */
2036     psp = g_pids;
2037     for (i = 0; i < g_npids; i++) {
2038         pp = psp->pid;
2039         pp->pid_index = 0xFFFFFFFF;
2040         psp++;
2041     }
2042
2043     ep = s_last_selected_event;
2044     code_to_chase = ep->code;
2045     datum_to_chase = ep->datum;
2046     pid_to_chase = ep->pid->pid_value;
2047     pids_mapped = 0;
2048     new_pidvec = g_malloc0(sizeof(pid_sort_t)*g_npids);
2049
2050     while (1) {
2051         if (srch_chase_dir == SRCH_CHASE_FORWARD) {
2052             if (ep >= g_events + g_nevents)
2053                 break;
2054         } else {
2055             if (ep < g_events)
2056                 break;
2057         }
2058
2059         winner = 0;
2060         switch(mode) {
2061         case CHASE_EVENT:
2062             if (ep->code == code_to_chase) {
2063                 winner = 1;
2064             }
2065             break;
2066
2067         case CHASE_DATUM:
2068             if (ep->datum == datum_to_chase) {
2069                 winner = 1;
2070             }
2071             break;
2072
2073         case CHASE_TRACK:
2074             if (ep->pid->pid_value == pid_to_chase) {
2075                 winner = 1;
2076             }
2077             break;
2078
2079         default:
2080             infobox("BUG", "unknown mode in chase_event_etc\n");
2081             break;
2082         }
2083
2084         if (winner) {
2085             if (ep->pid->pid_index == 0xFFFFFFFF) {
2086                 ep->pid->pid_index = pids_mapped;
2087                 new_pidvec[pids_mapped].pid = ep->pid;
2088                 new_pidvec[pids_mapped].pid_value = ep->pid->pid_value;
2089                 new_pidvec[pids_mapped].color_index = 0;
2090                 pids_mapped++;
2091                 if (pids_mapped == g_npids)
2092                     break;
2093             }
2094         }
2095         if (srch_chase_dir == SRCH_CHASE_FORWARD)
2096             ep++;
2097         else
2098             ep--;
2099     }
2100
2101     /* Pass 2, first-to-last, to collect stragglers */
2102     ep = g_events;
2103
2104     while (ep < g_events + g_nevents) {
2105         if (ep->pid->pid_index == 0xFFFFFFFF) {
2106             ep->pid->pid_index = pids_mapped;
2107             new_pidvec[pids_mapped].pid = ep->pid;
2108             new_pidvec[pids_mapped].pid_value = ep->pid->pid_value;
2109             new_pidvec[pids_mapped].color_index = 0;
2110             pids_mapped++;
2111             if (pids_mapped == g_npids)
2112                 break;
2113         }
2114         ep++;
2115     }
2116
2117     if (pids_mapped != g_npids) {
2118         infobox("BUG", "\nDidn't map all pids in chase_event_etc\n");
2119     }
2120
2121     g_free (g_pids);
2122     g_pids = new_pidvec;
2123
2124     /*
2125      * The new g_pids vector contains the "chase" sort, so we revert
2126      * the pid_index mapping to an identity map
2127      */
2128     psp = g_pids;
2129
2130     for (i = 0; i < g_npids; i++) {
2131         pp = psp->pid;
2132         pp->pid_index = i;
2133         psp++;
2134     }
2135
2136     /* AutoScroll the PID axis so we show the first "chased" event */
2137     s_v1->first_pid_index = 0;
2138     GTK_ADJUSTMENT(s_view1_vsadj)->value = 0.00;
2139     gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
2140     init_track_colors();
2141     view1_display_when_idle();
2142 }
2143
2144 /****************************************************************************
2145 * unchase_event_etc
2146 * Copy g_original_pids to g_pids, revert index mapping
2147 ****************************************************************************/
2148 static void unchase_event_etc(void)
2149 {
2150     int i;
2151     pid_sort_t *psp;
2152     pid_data_t *pp;
2153
2154     memcpy (g_pids, g_original_pids, sizeof(pid_sort_t)*g_npids);
2155
2156     /* Fix the pid structure index mappings */
2157     psp = g_pids;
2158
2159     for (i = 0; i < g_npids; i++) {
2160         pp = psp->pid;
2161         pp->pid_index = i;
2162         psp++;
2163     }
2164
2165     /* Scroll PID axis to the top */
2166     s_v1->first_pid_index = 0;
2167     GTK_ADJUSTMENT(s_view1_vsadj)->value = 0.00;
2168     gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
2169     init_track_colors();
2170     view1_display_when_idle();
2171 }
2172
2173 /****************************************************************************
2174 * print_ps_header
2175 * To fit a reasonable-sized landscape mode plot onto letter-size paper,
2176 * scale everything by .75.
2177 ****************************************************************************/
2178
2179 static void print_ps_header (v1_geometry_t *vp, char *filename)
2180 {
2181     time_t now;
2182
2183     now = time(0);
2184
2185     fprintf(s_printfp, "%%%%!PS-Adobe-3.0 EPSF-3.0\n");
2186     fprintf(s_printfp, "%%%%Creator: G2 Event Viewer\n");
2187     fprintf(s_printfp, "%%%%Title: %s\n", filename);
2188     fprintf(s_printfp, "%%%%CreationDate: %s", ctime(&now));
2189     fprintf(s_printfp, "%%%%DocumentData: Clean7Bit\n");
2190     fprintf(s_printfp, "%%%%Origin: 0 0\n");
2191     fprintf(s_printfp, "%%%%BoundingBox: 0 0 %d %d\n", vp->total_height,
2192            vp->total_width);
2193     fprintf(s_printfp, "%%%%LanguageLevel: 2\n");
2194     fprintf(s_printfp, "%%%%Pages: 1\n");
2195     fprintf(s_printfp, "%%%%Page: 1 1\n");
2196     fprintf(s_printfp, "%%%%EOF\n");
2197     fprintf(s_printfp, "/Times-Roman findfont\n");
2198     fprintf(s_printfp, "12 scalefont\n");
2199     fprintf(s_printfp, "setfont\n");
2200     fprintf(s_printfp, ".75 .75 scale\n");
2201 }
2202
2203 /****************************************************************************
2204 * xrt
2205 * Xcoordinate rotate and translate.  We need to emit postscript that
2206 * has a reasonable aspect ratio for printing.  To do that, we rotate the
2207 * intended picture by 90 degrees, using the standard 2D rotation
2208 * formula:
2209 *
2210 *     Xr = x*cos(theta) - y*sin(theta);
2211 *     Yr = x*sin(theta) + y*cos(theta);
2212 *
2213 * If we let theta = 90, this reduces to
2214 *     Xr = -y
2215 *     Yr =  x
2216 *
2217 * Translate back to the origin in X by adding Ymax, yielding
2218 *     Xrt = Ymax - y
2219 ****************************************************************************/
2220
2221 static inline int xrt(int x, int y)
2222 {
2223     return (s_v1->total_height - y);
2224 }
2225
2226 static inline int yrt(int x, int y)
2227 {
2228     return(x);
2229 }
2230
2231 /****************************************************************************
2232 * print_screen_callback
2233 ****************************************************************************/
2234
2235 static boolean print_screen_callback(char *filename)
2236 {
2237     s_printfp = fopen (filename, "wt");
2238
2239     if (s_printfp == NULL)
2240         return(FALSE);
2241
2242     /*
2243      * This variable allows us to magically turn the view1 display
2244      * code into a print-driver, with a minimum of fuss. The idea is to
2245      * magically change TBOX_DRAW_XXX into TBOX_PRINT_XXX by adding
2246      * the required value, aka s_print_offset.
2247      * Make sure to fix g2.h if you mess here, or vice versa.
2248      */
2249     s_print_offset = TBOX_PRINT_PLAIN - TBOX_DRAW_PLAIN;
2250
2251     print_ps_header(s_v1, filename);
2252
2253     display_pid_axis(s_v1);
2254     display_event_data(s_v1);
2255     display_time_axis(s_v1);
2256
2257     fclose (s_printfp);
2258     s_printfp = 0;
2259     s_print_offset = 0;
2260
2261     /* For tactile feedback */
2262     view1_display_when_idle();
2263     return(TRUE);
2264 }
2265
2266 int event_time_cmp (const void *a, const void *b)
2267 {
2268     const event_t *e1 = a;
2269     const event_t *e2 = b;
2270
2271     if (e1->time < e2->time)
2272         return -1;
2273     else if (e1->time > e2->time)
2274         return 1;
2275     return 0;
2276 }
2277
2278 /****************************************************************************
2279 * slew_tracks
2280 ****************************************************************************/
2281 static void slew_tracks (v1_geometry_t *vp, enum view1_button_click which)
2282 {
2283     event_t *ep;
2284     pid_sort_t *pp;
2285     int pid_index;
2286     ulonglong delta;
2287
2288     delta = (ulonglong) (vp->last_time_interval);
2289
2290     /* Make sure we don't push events to the left of the big bang */
2291     if (which == SLEW_LEFT_BUTTON) {
2292         for (ep = g_events; ep < (g_events + g_nevents); ep++) {
2293             pid_index = ep->pid->pid_index;
2294             pp = (g_pids + pid_index);
2295
2296             if (pp->selected) {
2297                 if (ep->time < delta) {
2298                     infobox("Slew Range Error",
2299                             "\nCan't slew selected data left that far..."
2300                             "\nEvents would preceed the Big Bang (t=0)...");
2301                     goto out;
2302                 }
2303             }
2304         }
2305     }
2306
2307     for (ep = g_events; ep < (g_events + g_nevents); ep++) {
2308         pid_index = ep->pid->pid_index;
2309         pp = (g_pids + pid_index);
2310
2311         if (pp->selected) {
2312             if (which == SLEW_LEFT_BUTTON)
2313                 ep->time -= delta;
2314             else
2315                 ep->time += delta;
2316         }
2317     }
2318
2319     /* Re-sort the events, to avoid screwing up the event display */
2320     qsort (g_events, g_nevents, sizeof(event_t), event_time_cmp);
2321
2322     /* De-select tracks */
2323     deselect_tracks();
2324
2325 out:
2326     view1_display_when_idle();
2327 }
2328
2329 /****************************************************************************
2330 * view1_button_click_callback
2331 ****************************************************************************/
2332
2333 static void view1_button_click_callback(GtkButton *item, gpointer data)
2334 {
2335     enum view1_button_click click = (enum view1_button_click) data;
2336     event_t *ep;
2337     ulonglong event_incdec;
2338     ulonglong current_width;
2339     ulonglong zoom_delta;
2340
2341     current_width = s_v1->maxvistime - s_v1->minvistime;
2342     event_incdec = (current_width) / 3;
2343
2344     if (event_incdec == 0LL)
2345         event_incdec = 1;
2346
2347     zoom_delta = (s_v1->maxvistime - s_v1->minvistime) / 6;
2348
2349     switch(click) {
2350     case TOP_BUTTON:
2351         /* First PID to top of window */
2352         s_v1->first_pid_index = 0;
2353         GTK_ADJUSTMENT(s_view1_vsadj)->value = 0.00;
2354         gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
2355         break;
2356
2357     case BOTTOM_BUTTON:
2358         s_v1->first_pid_index = g_npids - s_v1->npids;
2359         if (s_v1->first_pid_index < 0)
2360             s_v1->first_pid_index = 0;
2361         GTK_ADJUSTMENT(s_view1_vsadj)->value = (gdouble)s_v1->first_pid_index;
2362         gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
2363         break;
2364
2365     case SNAP_BUTTON:
2366         add_snapshot();
2367         break;
2368
2369     case NEXT_BUTTON:
2370         next_snapshot();
2371         break;
2372
2373     case DEL_BUTTON:
2374         del_snapshot();
2375         break;
2376
2377     case CHASE_EVENT_BUTTON:
2378         chase_event_etc(CHASE_EVENT);
2379         break;
2380
2381     case CHASE_DATUM_BUTTON:
2382         chase_event_etc(CHASE_DATUM);
2383         break;
2384
2385     case CHASE_TRACK_BUTTON:
2386         chase_event_etc(CHASE_TRACK);
2387         break;
2388
2389     case UNCHASE_BUTTON:
2390         unchase_event_etc();
2391         break;
2392
2393     case START_BUTTON:
2394     start_button:
2395         s_v1->minvistime = 0LL;
2396         s_v1->maxvistime = current_width;
2397         recompute_hscrollbar();
2398         break;
2399
2400     case ZOOMIN_BUTTON:
2401         s_v1->minvistime += zoom_delta;
2402         s_v1->maxvistime -= zoom_delta;
2403         recompute_hscrollbar();
2404         break;
2405
2406     case SEARCH_AGAIN_BUTTON:
2407         if (s_srchcode) {
2408             event_search_internal();
2409             break;
2410         }
2411         /* NOTE FALLTHROUGH */
2412
2413     case SEARCH_BUTTON:
2414         event_search();
2415         break;
2416
2417     case ANOMALY_THRESHOLD_BUTTON:
2418         anomaly_threshold();
2419         break;
2420
2421     case ANOMALY_NEXT_BUTTON:
2422         if (s_anomalycode) {
2423             anomaly_search_internal();
2424             break;
2425         }
2426         /* NOTE FALLTHROUGH */
2427
2428     case ANOMALY_BUTTON:
2429         anomaly_search();
2430         break;
2431
2432     case ZOOMOUT_BUTTON:
2433         if (zoom_delta == 0LL)
2434             zoom_delta = 1;
2435
2436         if (s_v1->minvistime >= zoom_delta) {
2437             s_v1->minvistime -= zoom_delta;
2438             s_v1->maxvistime += zoom_delta;
2439         } else {
2440             s_v1->minvistime = 0;
2441             s_v1->maxvistime += zoom_delta*2;
2442         }
2443
2444         if ((s_v1->maxvistime - s_v1->minvistime) * 8 >
2445             g_events[g_nevents-1].time * 9) {
2446             s_v1->minvistime = 0;
2447             s_v1->maxvistime = g_events[g_nevents-1].time * 9 / 8;
2448             /* Single event? Make window 1s wide... */
2449             if (g_nevents == 1)
2450                 s_v1->maxvistime = 1000000;
2451
2452         }
2453         recompute_hscrollbar();
2454         break;
2455
2456     case END_BUTTON:
2457         ep = (g_events + g_nevents - 1);
2458         s_v1->maxvistime = ep->time + event_incdec/3;
2459         s_v1->minvistime = s_v1->maxvistime - current_width;
2460         if (s_v1->minvistime > s_v1->maxvistime)
2461             goto start_button;
2462         recompute_hscrollbar();
2463         break;
2464
2465     case MORE_TRACES_BUTTON:
2466         /* Reduce the strip height to fit more traces on screen */
2467         s_v1->strip_height -= 1;
2468
2469         if (s_v1->strip_height < 1) {
2470             s_v1->strip_height = 1;
2471         }
2472
2473         /* Recalculate the number of strips on the screen */
2474         s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) /
2475             s_v1->strip_height;
2476         recompute_vscrollbar();
2477         break;
2478
2479     case LESS_TRACES_BUTTON:
2480         /* Increase the strip height to fit fewer on the screen */
2481         s_v1->strip_height += 1;
2482         if (s_v1->strip_height > 80) {
2483             s_v1->strip_height = 80;
2484         }
2485
2486         /* Recalculate the number of strips on the screen */
2487         s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) /
2488             s_v1->strip_height;
2489         recompute_vscrollbar();
2490         break;
2491
2492     case FORWARD_BUTTON:
2493         srch_chase_dir = SRCH_CHASE_FORWARD;
2494         gtk_widget_hide (s_view1_forward_button);
2495         gtk_widget_show (s_view1_backward_button);
2496         break;
2497
2498     case BACKWARD_BUTTON:
2499         srch_chase_dir = SRCH_CHASE_BACKWARD;
2500         gtk_widget_show (s_view1_forward_button);
2501         gtk_widget_hide (s_view1_backward_button);
2502         break;
2503
2504     case SUMMARY_BUTTON:
2505         summary_mode = TRUE;
2506         gtk_widget_hide (s_view1_summary_button);
2507         gtk_widget_show (s_view1_nosummary_button);
2508         break;
2509
2510     case NOSUMMARY_BUTTON:
2511         summary_mode = FALSE;
2512         gtk_widget_show (s_view1_summary_button);
2513         gtk_widget_hide (s_view1_nosummary_button);
2514         break;
2515
2516     case SLEW_LEFT_BUTTON:
2517     case SLEW_RIGHT_BUTTON:
2518         if (s_v1->last_time_interval < 10e-9) {
2519             infobox("slew", "\nNo time interval set...\n");
2520             break;
2521         }
2522         slew_tracks (s_v1, click);
2523         break;
2524     }
2525
2526     view1_display_when_idle();
2527 }
2528
2529 /****************************************************************************
2530 * view1_print_callback
2531 ****************************************************************************/
2532
2533 void view1_print_callback (GtkToggleButton *notused, gpointer nu2)
2534 {
2535     modal_dialog("Print Screen (PostScript format) to file:",
2536                  "Invalid file: Print Screen to file:",
2537                  "g2.ps", print_screen_callback);
2538 }
2539
2540 /****************************************************************************
2541 * view1_hscroll
2542 ****************************************************************************/
2543
2544 static void view1_hscroll (GtkAdjustment *adj, GtkWidget *notused)
2545 {
2546     ulonglong current_width;
2547
2548     current_width = (s_v1->maxvistime - s_v1->minvistime);
2549
2550     s_v1->minvistime = (ulonglong)(adj->value);
2551     s_v1->maxvistime = s_v1->minvistime + current_width;
2552
2553     view1_display_when_idle();
2554
2555 #ifdef NOTDEF
2556     g_print ("adj->lower = %.2f\n", adj->lower);
2557     g_print ("adj->upper = %.2f\n", adj->upper);
2558     g_print ("adj->value = %.2f\n", adj->value);
2559     g_print ("adj->step_increment = %.2f\n", adj->step_increment);
2560     g_print ("adj->page_increment = %.2f\n", adj->page_increment);
2561     g_print ("adj->page_size = %.2f\n", adj->page_size);
2562 #endif
2563 }
2564
2565 /****************************************************************************
2566 * view1_vscroll
2567 ****************************************************************************/
2568
2569 static void view1_vscroll (GtkAdjustment *adj, GtkWidget *notused)
2570 {
2571     s_v1->first_pid_index = (int)adj->value;
2572     view1_display_when_idle();
2573 }
2574
2575 void set_pid_ax_width(int width)
2576 {
2577     s_v1->pid_ax_width = width;
2578     view1_display_when_idle();
2579 }
2580
2581 /****************************************************************************
2582 * view1_init
2583 ****************************************************************************/
2584
2585 void view1_init(void)
2586 {
2587     c_view1_draw_width = atol(getprop_default("drawbox_width", "700"));
2588     c_view1_draw_height = atol(getprop_default("drawbox_height", "400"));
2589
2590     s_v1->pid_ax_width = 80;
2591     s_v1->time_ax_height = 80;
2592     s_v1->time_ax_spacing = 100;
2593     s_v1->strip_height = 25;
2594     s_v1->pop_offset = 20;
2595     s_v1->pid_ax_offset = 34;
2596     s_v1->event_offset = 40;
2597     s_v1->total_height = c_view1_draw_height;
2598     s_v1->total_width = c_view1_draw_width;
2599     s_v1->first_pid_index = 0;
2600     s_v1->anomaly_threshold_stddevs =
2601         atof(getprop_default("anomaly_threshold_stddevs", "2.5"));
2602     s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) /
2603         s_v1->strip_height;
2604
2605     s_v1->minvistime = 0;
2606     s_v1->maxvistime = 200;
2607
2608     s_view1_vbox = gtk_vbox_new(FALSE, 5);
2609
2610     s_view1_hbox = gtk_hbox_new(FALSE, 5);
2611
2612     da = gtk_drawing_area_new();
2613     gtk_drawing_area_size(GTK_DRAWING_AREA(da), c_view1_draw_width,
2614                           c_view1_draw_height);
2615
2616 #ifdef NOTDEF
2617     gtk_signal_connect (GTK_OBJECT (da), "motion_notify_event",
2618                         (GtkSignalFunc) motion_notify_event, NULL);
2619 #endif
2620
2621     gtk_signal_connect (GTK_OBJECT (da), "expose_event",
2622                         (GtkSignalFunc) expose_event, NULL);
2623
2624     gtk_signal_connect (GTK_OBJECT(da),"configure_event",
2625                         (GtkSignalFunc) configure_event, NULL);
2626
2627     gtk_signal_connect (GTK_OBJECT (da), "button_press_event",
2628                         (GtkSignalFunc) button_press_event, NULL);
2629
2630     gtk_signal_connect (GTK_OBJECT (da), "button_release_event",
2631                         (GtkSignalFunc) button_press_event, NULL);
2632
2633     gtk_signal_connect (GTK_OBJECT (da), "motion_notify_event",
2634                         (GtkSignalFunc) button_press_event, NULL);
2635
2636     gtk_widget_set_events (da, GDK_BUTTON_PRESS_MASK
2637                            | GDK_BUTTON_RELEASE_MASK | GDK_EXPOSURE_MASK
2638                            | GDK_BUTTON_MOTION_MASK);
2639
2640
2641     gtk_box_pack_start(GTK_BOX(s_view1_hbox), da, TRUE, TRUE, 0);
2642
2643     g_font = gdk_font_load ("8x13");
2644     if (g_font == NULL) {
2645         g_error("Couldn't load 8x13 font...\n");
2646     }
2647     gdk_font_ref(g_font);
2648
2649     /* PID axis menu */
2650     s_view1_vmenubox = gtk_vbox_new(FALSE, 5);
2651
2652     s_view1_vsadj = gtk_adjustment_new(0.0 /* initial value */,
2653                                        0.0 /* minimum value */,
2654                                        2000.0 /* maximum value */,
2655                                        0.1 /* step increment */,
2656                                        10.0/* page increment */,
2657                                        10.0/* page size */);
2658
2659     s_view1_vscroll = gtk_vscrollbar_new (GTK_ADJUSTMENT(s_view1_vsadj));
2660
2661     gtk_signal_connect (GTK_OBJECT (s_view1_vsadj), "value-changed",
2662                         GTK_SIGNAL_FUNC (view1_vscroll),
2663                         (gpointer)s_view1_vscroll);
2664
2665     s_view1_topbutton = gtk_button_new_with_label("Top");
2666     s_view1_bottombutton = gtk_button_new_with_label("Bottom");
2667
2668     gtk_signal_connect (GTK_OBJECT(s_view1_topbutton), "clicked",
2669                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2670                         (gpointer) TOP_BUTTON);
2671
2672     gtk_signal_connect (GTK_OBJECT(s_view1_bottombutton), "clicked",
2673                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2674                         (gpointer) BOTTOM_BUTTON);
2675
2676     /* More Traces button and Less Traces button */
2677     s_view1_more_traces_button = gtk_button_new_with_label("More Traces");
2678     s_view1_less_traces_button = gtk_button_new_with_label("Less Traces");
2679     gtk_signal_connect (GTK_OBJECT(s_view1_more_traces_button), "clicked",
2680                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2681                         (gpointer) MORE_TRACES_BUTTON);
2682     gtk_signal_connect (GTK_OBJECT(s_view1_less_traces_button), "clicked",
2683                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2684                         (gpointer) LESS_TRACES_BUTTON);
2685
2686 #ifdef NOTDEF
2687     /* Trick to bottom-justify the menu: */
2688     s_view1_pad1 = gtk_vbox_new(FALSE, 0);
2689     gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_pad1,
2690                         TRUE, FALSE, 0);
2691
2692 #endif
2693
2694     gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_topbutton,
2695                         FALSE, FALSE, 0);
2696
2697     gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_vscroll,
2698                         TRUE, TRUE, 0);
2699
2700     gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_bottombutton,
2701                         FALSE, FALSE, 0);
2702
2703     gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_more_traces_button,
2704                         FALSE, FALSE, 0);
2705
2706     gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_less_traces_button,
2707                         FALSE, FALSE, 0);
2708
2709     gtk_box_pack_start (GTK_BOX(s_view1_hbox), s_view1_vmenubox,
2710                         FALSE, FALSE, 0);
2711
2712     /* Time axis menu */
2713
2714     s_view1_hmenubox = gtk_hbox_new(FALSE, 5);
2715
2716     s_view1_startbutton = gtk_button_new_with_label("Start");
2717
2718     s_view1_zoominbutton = gtk_button_new_with_label("ZoomIn");
2719
2720     s_view1_searchbutton = gtk_button_new_with_label("Search");
2721     s_view1_srchagainbutton = gtk_button_new_with_label("Search Again");
2722
2723     s_view1_anomalybutton = gtk_button_new_with_label("Anomaly");
2724     s_view1_anomalynextbutton = gtk_button_new_with_label("Next Anomaly");
2725     s_view1_anomalythresholdbutton =
2726         gtk_button_new_with_label ("Anomaly Threshold");
2727
2728     s_view1_zoomoutbutton = gtk_button_new_with_label("ZoomOut");
2729
2730     s_view1_endbutton = gtk_button_new_with_label("End");
2731
2732     gtk_signal_connect (GTK_OBJECT(s_view1_startbutton), "clicked",
2733                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2734                         (gpointer) START_BUTTON);
2735
2736     gtk_signal_connect (GTK_OBJECT(s_view1_zoominbutton), "clicked",
2737                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2738                         (gpointer) ZOOMIN_BUTTON);
2739
2740     gtk_signal_connect (GTK_OBJECT(s_view1_searchbutton), "clicked",
2741                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2742                         (gpointer) SEARCH_BUTTON);
2743
2744     gtk_signal_connect (GTK_OBJECT(s_view1_srchagainbutton), "clicked",
2745                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2746                         (gpointer) SEARCH_AGAIN_BUTTON);
2747
2748     gtk_signal_connect (GTK_OBJECT(s_view1_anomalybutton), "clicked",
2749                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2750                         (gpointer) ANOMALY_BUTTON);
2751
2752     gtk_signal_connect (GTK_OBJECT(s_view1_anomalynextbutton), "clicked",
2753                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2754                         (gpointer) ANOMALY_NEXT_BUTTON);
2755
2756     gtk_signal_connect (GTK_OBJECT(s_view1_anomalythresholdbutton),
2757                         "clicked", GTK_SIGNAL_FUNC(view1_button_click_callback),
2758                         (gpointer) ANOMALY_THRESHOLD_BUTTON);
2759
2760     gtk_signal_connect (GTK_OBJECT(s_view1_zoomoutbutton), "clicked",
2761                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2762                         (gpointer) ZOOMOUT_BUTTON);
2763
2764     gtk_signal_connect (GTK_OBJECT(s_view1_endbutton), "clicked",
2765                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2766                         (gpointer) END_BUTTON);
2767
2768     s_view1_hsadj = gtk_adjustment_new(0.0 /* initial value */,
2769                                        0.0 /* minimum value */,
2770                                        2000.0 /* maximum value */,
2771                                        0.1 /* step increment */,
2772                                        10.0/* page increment */,
2773                                        10.0/* page size */);
2774
2775     s_view1_hscroll = gtk_hscrollbar_new (GTK_ADJUSTMENT(s_view1_hsadj));
2776
2777     gtk_signal_connect (GTK_OBJECT (s_view1_hsadj), "value-changed",
2778                         GTK_SIGNAL_FUNC (view1_hscroll),
2779                         (gpointer)s_view1_hscroll);
2780
2781     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_startbutton,
2782                         FALSE, FALSE, 0);
2783
2784     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_hscroll,
2785                         TRUE, TRUE, 0);
2786
2787     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_endbutton,
2788                         FALSE, FALSE, 0);
2789
2790     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_zoominbutton,
2791                         FALSE, FALSE, 0);
2792
2793     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_anomalybutton,
2794                         FALSE, FALSE, 0);
2795     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_anomalynextbutton,
2796                         FALSE, FALSE, 0);
2797     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_searchbutton,
2798                         FALSE, FALSE, 0);
2799
2800     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_srchagainbutton,
2801                         FALSE, FALSE, 0);
2802
2803     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_zoomoutbutton,
2804                         FALSE, FALSE, 0);
2805
2806     gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_hbox,
2807                         TRUE, TRUE, 0);
2808
2809     gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_hmenubox,
2810                         FALSE, FALSE, 0);
2811
2812
2813     s_view1_hmenubox2 = gtk_hbox_new(FALSE, 5);
2814
2815     s_view1_snapbutton = gtk_button_new_with_label("Snap");
2816
2817     s_view1_nextbutton = gtk_button_new_with_label("Next");
2818
2819     s_view1_delbutton = gtk_button_new_with_label("Del");
2820
2821     s_view1_chase_event_button = gtk_button_new_with_label("ChaseEvent");
2822
2823     s_view1_chase_datum_button = gtk_button_new_with_label("ChaseDatum");
2824
2825     s_view1_chase_track_button = gtk_button_new_with_label("ChaseTrack");
2826
2827     s_view1_unchasebutton = gtk_button_new_with_label("NoChase");
2828
2829     s_view1_forward_button = gtk_button_new_with_label("->SrchChase(is<-)");
2830     s_view1_backward_button = gtk_button_new_with_label("<-SrchChase(is->)");
2831
2832     s_view1_summary_button = gtk_button_new_with_label("Summary");
2833     s_view1_nosummary_button = gtk_button_new_with_label("NoSummary");
2834
2835     s_view1_time_slew_left_button = gtk_button_new_with_label("<-TimeSlew");
2836     s_view1_time_slew_right_button = gtk_button_new_with_label("TimeSlew->");
2837
2838     gtk_signal_connect (GTK_OBJECT(s_view1_snapbutton), "clicked",
2839                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2840                         (gpointer) SNAP_BUTTON);
2841
2842     gtk_signal_connect (GTK_OBJECT(s_view1_nextbutton), "clicked",
2843                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2844                         (gpointer) NEXT_BUTTON);
2845
2846     gtk_signal_connect (GTK_OBJECT(s_view1_delbutton), "clicked",
2847                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2848                         (gpointer) DEL_BUTTON);
2849
2850     gtk_signal_connect (GTK_OBJECT(s_view1_chase_event_button), "clicked",
2851                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2852                         (gpointer) CHASE_EVENT_BUTTON);
2853
2854     gtk_signal_connect (GTK_OBJECT(s_view1_chase_datum_button), "clicked",
2855                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2856                         (gpointer) CHASE_DATUM_BUTTON);
2857
2858     gtk_signal_connect (GTK_OBJECT(s_view1_chase_track_button), "clicked",
2859                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2860                         (gpointer) CHASE_TRACK_BUTTON);
2861
2862     gtk_signal_connect (GTK_OBJECT(s_view1_unchasebutton), "clicked",
2863                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2864                         (gpointer) UNCHASE_BUTTON);
2865
2866     gtk_signal_connect (GTK_OBJECT(s_view1_forward_button), "clicked",
2867                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2868                         (gpointer) FORWARD_BUTTON);
2869
2870     gtk_signal_connect (GTK_OBJECT(s_view1_backward_button), "clicked",
2871                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2872                         (gpointer) BACKWARD_BUTTON);
2873
2874     gtk_signal_connect (GTK_OBJECT(s_view1_summary_button), "clicked",
2875                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2876                         (gpointer) SUMMARY_BUTTON);
2877
2878     gtk_signal_connect (GTK_OBJECT(s_view1_nosummary_button), "clicked",
2879                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2880                         (gpointer) NOSUMMARY_BUTTON);
2881
2882     gtk_signal_connect (GTK_OBJECT(s_view1_time_slew_left_button), "clicked",
2883                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2884                         (gpointer) SLEW_LEFT_BUTTON);
2885
2886     gtk_signal_connect (GTK_OBJECT(s_view1_time_slew_right_button), "clicked",
2887                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2888                         (gpointer) SLEW_RIGHT_BUTTON);
2889
2890     gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_hmenubox2,
2891                         FALSE, FALSE, 0);
2892
2893     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_snapbutton,
2894                         FALSE, FALSE, 0);
2895
2896     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_nextbutton,
2897                         FALSE, FALSE, 0);
2898
2899     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_delbutton,
2900                         FALSE, FALSE, 0);
2901
2902     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_chase_event_button,
2903                         FALSE, FALSE, 0);
2904
2905     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_chase_datum_button,
2906                         FALSE, FALSE, 0);
2907
2908     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_chase_track_button,
2909                         FALSE, FALSE, 0);
2910
2911     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_unchasebutton,
2912                         FALSE, FALSE, 0);
2913
2914     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_forward_button,
2915                         FALSE, FALSE, 0);
2916
2917     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_backward_button,
2918                         FALSE, FALSE, 0);
2919
2920     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_summary_button,
2921                         FALSE, FALSE, 0);
2922
2923     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_nosummary_button,
2924                         FALSE, FALSE, 0);
2925
2926     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2),
2927                         s_view1_time_slew_left_button,
2928                         FALSE, FALSE, 0);
2929
2930     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2),
2931                         s_view1_time_slew_right_button,
2932                         FALSE, FALSE, 0);
2933
2934     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2),
2935                         s_view1_anomalythresholdbutton,
2936                         FALSE, FALSE, 0);
2937
2938     s_view1_label = gtk_label_new(NULL);
2939
2940     gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_label,
2941                         FALSE, FALSE, 0);
2942
2943     gtk_box_pack_start (GTK_BOX(g_mainhbox), s_view1_vbox,
2944                         TRUE, TRUE, 0);
2945
2946     gtk_widget_show_all (s_view1_vbox);
2947     GTK_WIDGET_SET_FLAGS(da, GTK_CAN_FOCUS);
2948     gtk_widget_grab_focus(da);
2949
2950     gtk_widget_hide (s_view1_forward_button);
2951     gtk_widget_hide (summary_mode ? s_view1_summary_button
2952                                   : s_view1_nosummary_button);
2953
2954     zi_source = gdk_bitmap_create_from_data (NULL, (char *)zi_bits, zi_width,
2955                                              zi_height);
2956     zi_mask = gdk_bitmap_create_from_data (NULL, (char *)zi_bkgd, zi_width,
2957                                            zi_height);
2958
2959     zi_cursor = (GdkCursor *) gdk_cursor_new_from_pixmap (zi_source,
2960                                                           zi_mask, &fg_black,
2961                                                           &bg_white, zi_x_hot,
2962                                                           zi_y_hot);
2963     gdk_pixmap_unref (zi_source);
2964     gdk_pixmap_unref (zi_mask);
2965
2966     norm_cursor = (GdkCursor *) gdk_cursor_new (GDK_TOP_LEFT_ARROW);
2967 }
2968
2969 /****************************************************************************
2970 * line_print
2971 ****************************************************************************/
2972
2973 void line_print (int x1, int y1, int x2, int y2)
2974 {
2975     fprintf(s_printfp, "newpath\n");
2976     fprintf(s_printfp, "%d %d moveto\n", xrt(x1, s_v1->total_height - y1),
2977             yrt(x1, s_v1->total_height - y1));
2978
2979     fprintf(s_printfp, "%d %d lineto\n", xrt (x2, s_v1->total_height - y2),
2980             yrt (x2, s_v1->total_height - y2));
2981     fprintf(s_printfp, "1 setlinewidth\n");
2982     fprintf(s_printfp, "stroke\n");
2983 }
2984
2985 /****************************************************************************
2986 * tbox_print
2987 ****************************************************************************/
2988 GdkRectangle *tbox_print (char *s, int x, int y, enum view1_tbox_fn function,
2989                           GdkRectangle *rp)
2990 {
2991     if (function == TBOX_PRINT_BOXED) {
2992         rp->width -= 4;
2993     }
2994
2995     if ((function == TBOX_PRINT_BOXED) ||
2996         (function == TBOX_PRINT_EVENT)) {
2997
2998         fprintf(s_printfp, "newpath\n");
2999         fprintf(s_printfp, "0 setlinewidth\n");
3000         fprintf(s_printfp, "%d %d moveto\n",
3001                 xrt(rp->x, s_v1->total_height - rp->y),
3002                 yrt(rp->x, s_v1->total_height - rp->y));
3003
3004         fprintf(s_printfp, "%d %d lineto\n",
3005                 xrt (rp->x+rp->width, s_v1->total_height - rp->y),
3006                 yrt (rp->x+rp->width, s_v1->total_height - rp->y));
3007
3008         fprintf(s_printfp, "%d %d lineto\n",
3009                 xrt(rp->x+rp->width, s_v1->total_height - (rp->y+rp->height)),
3010                 yrt(rp->x+rp->width, s_v1->total_height - (rp->y+rp->height)));
3011
3012         fprintf(s_printfp, "%d %d lineto\n",
3013                 xrt(rp->x, s_v1->total_height - (rp->y+rp->height)),
3014                 yrt(rp->x, s_v1->total_height - (rp->y+rp->height)));
3015
3016         fprintf(s_printfp, "%d %d lineto\n",
3017                 xrt(rp->x, s_v1->total_height - rp->y),
3018                 yrt(rp->x, s_v1->total_height - rp->y));
3019
3020         fprintf(s_printfp, "stroke\n");
3021     }
3022
3023     if ((function == TBOX_PRINT_BOXED) ||
3024         (function == TBOX_PRINT_PLAIN)) {
3025
3026         fprintf(s_printfp, "newpath\n");
3027         fprintf(s_printfp, "%d %d moveto\n",
3028                 xrt(x, s_v1->total_height - (y-2)),
3029                 yrt(x, s_v1->total_height - (y-2)));
3030         fprintf(s_printfp, "gsave\n");
3031         fprintf(s_printfp, "90 rotate\n");
3032         fprintf(s_printfp, "(%s) show\n", s);
3033         fprintf(s_printfp, "grestore\n");
3034     }
3035
3036     return(rp);
3037 }
3038
3039 /****************************************************************************
3040 * tbox - draws an optionally boxed string whose lower lefthand
3041 * corner is at (x, y).  As usual, Y is backwards.
3042 ****************************************************************************/
3043
3044 GdkRectangle *tbox (char *s, int x, int y, enum view1_tbox_fn function)
3045 {
3046     static GdkRectangle update_rect;
3047     gint lbearing, rbearing, width, ascent, descent;
3048
3049     gdk_string_extents (g_font, s,
3050                         &lbearing, &rbearing,
3051                         &width, &ascent, &descent);
3052
3053     /*
3054      * If we have enough room to display full size events, then just
3055      * use the BOXED function instead of the EVENT function.
3056      */
3057     if (s_v1->strip_height > 9) {
3058         switch (function) {
3059         case TBOX_DRAW_EVENT:    function = TBOX_DRAW_BOXED;    break;
3060         case TBOX_GETRECT_EVENT: function = TBOX_GETRECT_BOXED; break;
3061         case TBOX_PRINT_EVENT:   function = TBOX_PRINT_BOXED;   break;
3062         default:
3063             break;
3064             /* Nothing */
3065         }
3066     }
3067
3068     switch (function) {
3069     case TBOX_DRAW_BOXED:
3070         gdk_draw_rectangle (pm, da->style->white_gc, TRUE,
3071                             x, y - (ascent+descent+3), width + 2,
3072                             ascent + descent + 3);
3073
3074         gdk_draw_rectangle (pm, da->style->black_gc, FALSE,
3075                             x, y - (ascent+descent+3), width + 2,
3076                             ascent + descent + 3);
3077
3078         gdk_draw_string (pm, g_font, da->style->black_gc,
3079                          x + 1, y - 1, (const gchar *)s);
3080         /* NOTE FALLTHROUGH */
3081     case TBOX_GETRECT_BOXED:
3082         update_rect.x = x;
3083         update_rect.y = y -(ascent+descent+3);
3084         update_rect.width = width + 3;
3085         update_rect.height = ascent + descent + 4;
3086         if (function == TBOX_DRAW_BOXED)
3087             gtk_widget_draw (da, &update_rect);
3088         break;
3089
3090     case TBOX_DRAW_EVENT:
3091         /* We have a small event to draw...no text */
3092         gdk_draw_rectangle (pm, da->style->black_gc, FALSE,
3093                             x, y - 1, 3, 3);
3094         /* NOTE FALLTHROUGH */
3095     case TBOX_GETRECT_EVENT:
3096         update_rect.x = x;
3097         update_rect.y = y - 1;
3098         update_rect.width = 4;
3099         update_rect.height = 4;
3100         if (function == TBOX_DRAW_EVENT)
3101             gtk_widget_draw (da, &update_rect);
3102         break;
3103
3104
3105     case TBOX_DRAW_PLAIN:
3106
3107         gdk_draw_string (pm, g_font, da->style->black_gc,
3108                          x + 1, y - 1, (const gchar *)s);
3109         /* NOTE FALLTHROUGH */
3110     case TBOX_GETRECT_PLAIN:
3111         update_rect.x = x;
3112         update_rect.y = y -(ascent+descent+1);
3113         update_rect.width = width;
3114         update_rect.height = ascent + descent;
3115         if (function == TBOX_DRAW_PLAIN)
3116             gtk_widget_draw (da, &update_rect);
3117         break;
3118
3119     case TBOX_PRINT_BOXED:
3120         update_rect.x = x;
3121         update_rect.y = y -(ascent+descent+3);
3122         update_rect.width = width + 3;
3123         update_rect.height = ascent + descent + 4;
3124         /* note fallthrough */
3125     case TBOX_PRINT_PLAIN:
3126         return(tbox_print(s, x, y, function, &update_rect));
3127
3128     case TBOX_PRINT_EVENT:
3129         /* We have a small event box to print...no text */
3130         update_rect.x = x;
3131         update_rect.y = y - 1;
3132         update_rect.width = 4;
3133         update_rect.height = 4;
3134         return(tbox_print(s, x, y, function, &update_rect));
3135     }
3136     return(&update_rect);
3137 }
3138
3139 /****************************************************************************
3140 * line
3141 *
3142 * For lines there is a primitive batching facility, that doesn't update
3143 * the drawing area until the batch is complete. This is handy for drawing
3144 * the pid axis and for summary mode.
3145 *
3146 * line_batch_mode contains the state for this:
3147 *
3148 *   BATCH_OFF:      no batching, update for every line
3149 *   BATCH_NEW:      just entered a batch, so initialize the area to update from
3150 *                   scratch
3151 *   BATCH_EXISTING: have drawn at least one line in batch mode, so the update
3152 *                   area should only be expanded from now on to include the
3153 *                   union of the "rectangular hull" of all lines
3154 ****************************************************************************/
3155
3156 static enum { BATCH_OFF, BATCH_NEW, BATCH_EXISTING } line_batch_mode;
3157 static int line_batch_count;
3158 static int line_minx, line_miny, line_maxx, line_maxy;
3159
3160 void line_batch_start (void)
3161 {
3162     line_batch_mode = BATCH_NEW;
3163     line_batch_count = 0;
3164 }
3165
3166 void line_batch_end (void)
3167 {
3168     GdkRectangle update_rect;
3169     if (line_batch_count > 0) {
3170         update_rect.x = line_minx;
3171         update_rect.y = line_miny;
3172         update_rect.width = (line_maxx - line_minx) + 1;
3173         update_rect.height = (line_maxy - line_miny) + 1;
3174         gtk_widget_draw (da, &update_rect);
3175     }
3176     line_batch_mode = BATCH_OFF;
3177 }
3178
3179 void line (int x1, int y1, int x2, int y2, enum view1_line_fn function)
3180 {
3181     GdkRectangle update_rect;
3182     GdkGC *gc = NULL;
3183
3184     switch(function) {
3185     case LINE_DRAW_BLACK:
3186         gc = da->style->black_gc;
3187         break;
3188
3189     case LINE_DRAW_WHITE:
3190         gc = da->style->white_gc;
3191         break;
3192
3193     case LINE_PRINT:
3194         line_print (x1, y1, x2, y2);
3195         return;
3196     }
3197
3198     gdk_draw_line (pm, gc, x1, y1, x2, y2);
3199
3200     switch (line_batch_mode) {
3201         case BATCH_OFF:
3202             update_rect.x = x1;
3203             update_rect.y = y1;
3204             update_rect.width = (x2-x1) + 1;
3205             update_rect.height = (y2-y1) + 1;
3206             gtk_widget_draw (da, &update_rect);
3207             break;
3208
3209         case BATCH_NEW:
3210             line_minx = x1;
3211             line_maxx = x2;
3212             line_miny = y1;
3213             line_maxy = y2;
3214             line_batch_mode = BATCH_EXISTING;
3215             line_batch_count = 1;
3216             break;
3217
3218         case BATCH_EXISTING:
3219             if (line_minx > x1)
3220                 line_minx = x1;
3221             if (line_miny > y1)
3222                 line_miny = y1;
3223             if (line_maxx < x2)
3224                 line_maxx = x2;
3225             if (line_maxy < y2)
3226                 line_maxy = y2;
3227             line_batch_count++;
3228             break;
3229     }
3230 }
3231
3232
3233 /****************************************************************************
3234 * display_pid_axis
3235 ****************************************************************************/
3236
3237 static void display_pid_axis(v1_geometry_t *vp)
3238 {
3239     int y, i, label_tick;
3240     int last_printed_y = -vp->strip_height;
3241     pid_sort_t *pp;
3242     int pid_index;
3243     char *label_fmt;
3244     char tmpbuf [128];
3245
3246     /* No pids yet? Outta here */
3247     if (g_pids == NULL)
3248         return;
3249
3250     line_batch_start();
3251
3252     for (i = 0; i < vp->npids; i++) {
3253         pid_index = vp->first_pid_index + i;
3254         if (pid_index >= g_npids)
3255             break;
3256
3257         pp = (g_pids + pid_index);
3258
3259         set_color(pid_index);
3260
3261         label_fmt = get_track_label(pp->pid_value);
3262         snprintf(tmpbuf, sizeof(tmpbuf)-1, label_fmt, pp->pid_value);
3263
3264         y = i*vp->strip_height + vp->pid_ax_offset;
3265
3266         /*
3267          * Have we incremented enough space to have another label not
3268          * overlap the previous label?
3269          */
3270         if (y - last_printed_y > 9) {
3271             /* Draw label */
3272             tbox(tmpbuf, 0, y +4, TBOX_DRAW_PLAIN+s_print_offset);
3273
3274             last_printed_y = y;
3275
3276             /*
3277              * And let the line stick out a bit more to indicate this label
3278              * relates to the following line.
3279              */
3280             label_tick = 4;
3281         }
3282         else {
3283             label_tick = 0;
3284         }
3285
3286         /* Draw axis line, but only if the lines aren't too close together */
3287         if (vp->strip_height > 4) {
3288             line(vp->pid_ax_width - label_tick, y+4*s_print_offset,
3289                  vp->total_width, y+4*s_print_offset,
3290                  LINE_DRAW_BLACK+s_print_offset);
3291         }
3292     }
3293
3294     set_color(COLOR_DEFAULT);
3295     line_batch_end();
3296 }
3297
3298 /****************************************************************************
3299 * view1_read_events_callback
3300 * New event data just showed up, reset a few things.
3301 ****************************************************************************/
3302
3303 void view1_read_events_callback(void)
3304 {
3305     int max_vis_index;
3306
3307     s_v1->first_pid_index = 0;
3308
3309     max_vis_index = 300;
3310     if (max_vis_index > g_nevents)
3311         max_vis_index = g_nevents-1;
3312
3313     s_v1->minvistime = 0LL;
3314     s_v1->maxvistime = (g_events[g_nevents - 1].time * 9)/ 8;
3315     /* Single event? Make the initial display 1s wide */
3316     if (g_nevents == 1)
3317         s_v1->maxvistime = 1000000;
3318     s_srchindex = 0;
3319     s_srchcode = 0;
3320     s_last_selected_event = 0;
3321
3322     init_track_colors();
3323
3324     recompute_hscrollbar();
3325     recompute_vscrollbar();
3326 }
3327
3328 /****************************************************************************
3329 * display_event_data
3330 ****************************************************************************/
3331
3332 static void display_event_data(v1_geometry_t *vp)
3333 {
3334     int start_index;
3335     int pid_index;
3336     int x, y;
3337     event_t *ep;
3338     event_def_t *edp;
3339     double time_per_pixel;
3340     char tmpbuf[1024];
3341     GdkRectangle *print_rect;
3342     int *last_x_used;
3343
3344     /* Happens if one loads the event def header first, for example. */
3345     if (g_nevents == 0)
3346         return;
3347
3348     time_per_pixel = dtime_per_pixel(vp);
3349
3350     start_index = find_event_index (vp->minvistime);
3351
3352     /* Scrolled too far right? */
3353     if (start_index >= g_nevents)
3354         return;
3355
3356     ep = (g_events + start_index);
3357
3358     if (s_print_offset || summary_mode) {
3359         last_x_used = (int *)g_malloc0(vp->npids * sizeof(int));
3360     } else {
3361         last_x_used = NULL;
3362     }
3363
3364     line_batch_start();
3365
3366     while (ep < (g_events + g_nevents) &&
3367            (ep->time < vp->maxvistime)) {
3368         pid_index = ep->pid->pid_index;
3369         set_color(pid_index);
3370
3371         /* First filter: pid out of range */
3372         if ((pid_index < vp->first_pid_index) ||
3373             (pid_index >= vp->first_pid_index + vp->npids)) {
3374             ep++;
3375             continue;
3376         }
3377
3378         /* Second filter: event hidden */
3379         edp = find_event_definition (ep->code);
3380         if (!edp->selected) {
3381             ep++;
3382             continue;
3383         }
3384
3385         /* Display it... */
3386
3387         pid_index -= vp->first_pid_index;
3388
3389         y = pid_index*vp->strip_height + vp->event_offset;
3390
3391         x = vp->pid_ax_width +
3392             (int)(((double)(ep->time - vp->minvistime)) / time_per_pixel);
3393
3394         if (last_x_used != NULL && x < last_x_used[pid_index]) {
3395             ep++;
3396             continue;
3397         }
3398
3399         if (ep->flags & (EVENT_FLAG_SELECT | EVENT_FLAG_SEARCHRSLT)) {
3400             if (ep->flags & EVENT_FLAG_SELECT) {
3401                 format_popbox_string(tmpbuf, sizeof(tmpbuf), ep, edp);
3402 #ifdef NOTDEF
3403                 sprintf(tmpbuf, edp->name);
3404                 sprintf(tmpbuf+strlen(tmpbuf), ": ");
3405                 sprintf(tmpbuf+strlen(tmpbuf), edp->format, ep->datum);
3406 #endif
3407             } else {
3408                 sprintf(tmpbuf, "SEARCH RESULT");
3409             }
3410             print_rect = tbox(tmpbuf, x, y - vp->pop_offset,
3411                               TBOX_DRAW_BOXED+s_print_offset);
3412             line(x, y-vp->pop_offset, x, y, LINE_DRAW_BLACK+s_print_offset);
3413             if (last_x_used != NULL)
3414                 last_x_used[pid_index] = x + print_rect->width;
3415         }
3416         if (summary_mode) {
3417             int delta = vp->strip_height / 3;
3418             if (delta < 1)
3419                 delta = 1;
3420             y = pid_index*vp->strip_height + vp->pid_ax_offset;
3421             line(x, y - delta, x, y + delta, LINE_DRAW_BLACK);
3422             last_x_used[pid_index] = x + 1;
3423         } else {
3424             sprintf(tmpbuf, "%ld", ep->code);
3425             print_rect = tbox(tmpbuf, x, y, TBOX_DRAW_EVENT+s_print_offset);
3426             if (last_x_used != NULL)
3427                 last_x_used[pid_index] = x + print_rect->width;
3428         }
3429
3430         ep++;
3431     }
3432     if (last_x_used)
3433         g_free(last_x_used);
3434     line_batch_end();
3435     set_color(COLOR_DEFAULT);
3436 }
3437
3438 /****************************************************************************
3439 * display_clear
3440 ****************************************************************************/
3441
3442 static void display_clear(void)
3443 {
3444     GdkRectangle update_rect;
3445
3446     gdk_draw_rectangle (pm, da->style->white_gc, TRUE,
3447                         0, 0, da->allocation.width,
3448                         da->allocation.height);
3449
3450     update_rect.x = 0;
3451     update_rect.y = 0;
3452     update_rect.width = da->allocation.width;
3453     update_rect.height = da->allocation.height;
3454
3455     gtk_widget_draw (da, &update_rect);
3456 }
3457
3458 /****************************************************************************
3459 * display_time_axis
3460 ****************************************************************************/
3461
3462 static void display_time_axis(v1_geometry_t *vp)
3463 {
3464     int x, y, i;
3465     int xoffset, nticks;
3466     char tmpbuf [128];
3467     double unit_divisor;
3468     double time;
3469     char *units;
3470     double time_per_pixel;
3471
3472     y = vp->npids * vp->strip_height + vp->pid_ax_offset;
3473
3474     x = vp->pid_ax_width;
3475
3476     nticks = (vp->total_width - vp->pid_ax_width) / vp->time_ax_spacing;
3477
3478     time_per_pixel = dtime_per_pixel(vp);
3479
3480     units = "ns";
3481     unit_divisor = 1.00;
3482
3483     if ((vp->maxvistime / unit_divisor) > 1000) {
3484         units = "us";
3485         unit_divisor = 1000.00;
3486     }
3487
3488     if ((vp->maxvistime / unit_divisor) > 1000) {
3489         units = "ms";
3490         unit_divisor = 1000.00*1000.00;
3491     }
3492     if ((vp->maxvistime / unit_divisor) > 1000) {
3493         units = "s";
3494         unit_divisor = 1000.00*1000.00*1000.00;
3495     }
3496
3497     /* Draw line */
3498     line(x, y, vp->total_width, y, LINE_DRAW_BLACK+s_print_offset);
3499
3500     xoffset = 0;
3501
3502     for (i = 0; i < nticks; i++) {
3503         /* Tick mark */
3504         line(x+xoffset, y-3, x+xoffset, y+3, LINE_DRAW_BLACK+s_print_offset);
3505
3506         time = (double)(x + xoffset - vp->pid_ax_width);
3507         time *= time_per_pixel;
3508         time += (double)(vp->minvistime);
3509         time /= unit_divisor;
3510
3511         sprintf (tmpbuf, "%.2f%s", time, units);
3512
3513         tbox(tmpbuf, x+xoffset, y+15, TBOX_DRAW_PLAIN+s_print_offset);
3514
3515         xoffset += vp->time_ax_spacing;
3516     }
3517 }
3518
3519 /****************************************************************************
3520 * clear_scoreboard
3521 * Forget about any temporary displays, they're gone now...
3522 ****************************************************************************/
3523
3524 static void clear_scoreboard(void)
3525 {
3526     s_result_up = FALSE;
3527 }
3528
3529 /****************************************************************************
3530 * view1_display
3531 ****************************************************************************/
3532
3533 void view1_display(void)
3534 {
3535     display_clear();
3536     display_pid_axis(s_v1);
3537     display_event_data(s_v1);
3538     display_time_axis(s_v1);
3539     clear_scoreboard();
3540 }
3541
3542 static gint idle_tag;
3543
3544 /****************************************************************************
3545 * view1_display_eventually
3546 ****************************************************************************/
3547
3548 static void view1_display_eventually(void)
3549 {
3550     gtk_idle_remove(idle_tag);
3551     idle_tag = 0;
3552     view1_display();
3553 }
3554
3555
3556 /****************************************************************************
3557 * view1_display_when_idle
3558 ****************************************************************************/
3559
3560 void view1_display_when_idle(void)
3561 {
3562     if (idle_tag == 0) {
3563         idle_tag = gtk_idle_add((GtkFunction) view1_display_eventually, 0);
3564     }
3565 }
3566
3567 /****************************************************************************
3568 * view1_about
3569 ****************************************************************************/
3570
3571 void view1_about (char *tmpbuf)
3572 {
3573     int nsnaps;
3574     snapshot_t *snaps;
3575
3576     sprintf(tmpbuf+strlen(tmpbuf), "Minvistime %lld\nMaxvistime %lld\n",
3577             s_v1->minvistime, s_v1->maxvistime);
3578     sprintf(tmpbuf+strlen(tmpbuf), "Strip Height %d\n",
3579             s_v1->strip_height);
3580
3581     for (nsnaps = 0, snaps = s_snapshots; snaps; snaps = snaps->next) {
3582         nsnaps++;
3583     }
3584     sprintf(tmpbuf+strlen(tmpbuf), "%d snapshots in the ring\n", nsnaps);
3585 }