a0cff900dbf91d1cc4e78869247e463361f2f23e
[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     f64 fdata;
1634
1635     /* Gather summary statistics... */
1636     ep = g_events;
1637
1638     vec_reset_length (s_v1->means);
1639     vec_reset_length (s_v1->matches);
1640     vec_reset_length (s_v1->variances);
1641     vec_reset_length (s_v1->two_stddevs);
1642     vec_reset_length (s_v1->mins);
1643     vec_reset_length (s_v1->maxes);
1644
1645     for (i = 0; i < g_nevents; i++) {
1646         if (ep->code != s_anomalycode) {
1647             ep++;
1648             continue;
1649         }
1650         pid = ep->pid;
1651         vec_validate_init_empty (s_v1->matches, pid->pid_index, 0);
1652         vec_validate_init_empty (s_v1->means, pid->pid_index, 0.0);
1653         vec_validate_init_empty (s_v1->mins, pid->pid_index, 0.0);
1654         vec_validate_init_empty (s_v1->maxes, pid->pid_index, 0.0);
1655         eep = get_clib_event (ep->datum);
1656         data = clib_mem_unaligned (eep->data, u32);
1657         fdata = data;
1658         s_v1->means[pid->pid_index] += fdata;
1659         s_v1->matches[pid->pid_index] += 1;
1660         /* First data point? set min, max */
1661         if (PREDICT_FALSE(s_v1->matches[pid->pid_index] == 1)) {
1662             s_v1->mins[pid->pid_index] = fdata;
1663             s_v1->maxes[pid->pid_index] = fdata;
1664         } else {
1665             s_v1->mins[pid->pid_index] = (fdata < s_v1->mins[pid->pid_index]) ?
1666                 fdata : s_v1->mins[pid->pid_index];
1667             s_v1->maxes[pid->pid_index] =
1668                 (fdata > s_v1->maxes[pid->pid_index]) ?
1669                 fdata : s_v1->maxes[pid->pid_index];
1670         }
1671         ep++;
1672     }
1673     if (vec_len (s_v1->matches) == 0)
1674         return -1;
1675
1676     /* Compute s_v1->means */
1677     for (i = 0; i < vec_len (s_v1->means); i++)
1678         s_v1->means[i] = s_v1->matches[i]
1679             ? (s_v1->means[i] / (f64) s_v1->matches[i]) : 0.0;
1680
1681     /* Compute s_v1->variances */
1682     ep = g_events;
1683     for (i = 0; i < g_nevents; i++) {
1684         if (ep->code != s_anomalycode) {
1685             ep++;
1686             continue;
1687         }
1688         pid = ep->pid;
1689         vec_validate_init_empty (s_v1->variances, pid->pid_index, 0);
1690         eep = get_clib_event (ep->datum);
1691         data = clib_mem_unaligned (eep->data, u32);
1692         fdata = data;
1693         s_v1->variances[pid->pid_index] +=
1694             (fdata - s_v1->means[pid->pid_index])
1695             * (fdata - s_v1->means[pid->pid_index]);
1696         ep++;
1697     }
1698
1699     /* Normalize variances */
1700     for (i = 0; i < vec_len (s_v1->variances); i++)
1701         s_v1->variances[i] = s_v1->matches[i]
1702             ? (s_v1->variances[i] / (f64) s_v1->matches[i]) : 0.0;
1703
1704     /* Compute the anomaly threshold, by default 2.5*stddev */
1705     for (i = 0; i < vec_len (s_v1->variances); i++)
1706         vec_add1 (s_v1->two_stddevs,
1707                   s_v1->anomaly_threshold_stddevs * sqrt(s_v1->variances[i]));
1708     return 0;
1709 }
1710
1711 /****************************************************************************
1712 * anomaly_search_internal
1713 * This routine searches forward from s_srchindex, looking for s_srchcode;
1714 * wraps at the end of the buffer.
1715 ****************************************************************************/
1716
1717 boolean anomaly_search_internal (void)
1718 {
1719     elog_event_t *eep;
1720     u32 data;
1721     event_t *ep;
1722     pid_data_t *pid;
1723     int i;
1724     int index;
1725     int pid_index;
1726     boolean full_redisplay = FALSE;
1727     ulonglong current_width;
1728     char tmpbuf [64];
1729     f64 fdata;
1730
1731     if (vec_len (s_v1->matches) == 0)
1732         anomaly_statistics_init();
1733
1734     ep = (g_events + s_srchindex);
1735     ep->flags &= ~EVENT_FLAG_SEARCHRSLT;
1736
1737     /*
1738      * If the user rearranged the screen, start from the minimum
1739      * visible time
1740      */
1741     if (ep->time < s_v1->minvistime)
1742         s_srchindex = find_event_index (s_v1->minvistime);
1743
1744     for (i = 1; i <= g_nevents; i++) {
1745         index = (i + s_srchindex) % g_nevents;
1746
1747         ep = (g_events + index);
1748         if (ep->code != s_anomalycode)
1749             continue;
1750         pid = ep->pid;
1751
1752         eep = get_clib_event (ep->datum);
1753         data = clib_mem_unaligned (eep->data, u32);
1754         fdata = data;
1755
1756         /*
1757          * Found an anomaly? Define an anomaly as a datum
1758          * greater than 2*stddev above average.
1759          */
1760         if ((fdata - s_v1->means[pid->pid_index]) >
1761             s_v1->two_stddevs[pid->pid_index]) {
1762             u8 *s;
1763
1764             s = format (0, "%.1f*stddev {min,max,mean,threshold}: ",
1765                         s_v1->anomaly_threshold_stddevs);
1766
1767             for (i = 0; i < vec_len (s_v1->means); i++) {
1768                 if (s_v1->matches[i] > 0)
1769                     s = format (s, "{%.0f, %.0f, %.0f, %.0f} ",
1770                                 s_v1->mins[i], s_v1->maxes[i],
1771                                 s_v1->means[i],
1772                                 s_v1->means[i]+s_v1->two_stddevs[i]);
1773                 else
1774                     s = format (s, "{no match} ");
1775             }
1776
1777             message_line ((char *)s);
1778             vec_free (s);
1779
1780             s_srchindex = index;
1781             pid_index = ep->pid->pid_index;
1782
1783             /* Need a vertical scroll? */
1784             if ((pid_index < s_v1->first_pid_index) ||
1785                 (pid_index >= s_v1->first_pid_index + s_v1->npids)) {
1786                 if (pid_index > (g_npids - s_v1->npids))
1787                     pid_index = (g_npids - s_v1->npids);
1788                 s_v1->first_pid_index = pid_index;
1789                 GTK_ADJUSTMENT(s_view1_vsadj)->value =
1790                     (gdouble)s_v1->first_pid_index;
1791                 gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
1792                 full_redisplay = TRUE;
1793             }
1794
1795             /* Need a horizontal scroll? */
1796             if (ep->time < s_v1->minvistime || ep->time > s_v1->maxvistime) {
1797                 current_width = (s_v1->maxvistime - s_v1->minvistime);
1798                 if (ep->time < ((current_width+1) / 2)) {
1799                     s_v1->minvistime = 0ll;
1800                     s_v1->maxvistime = current_width;
1801                 } else {
1802                     s_v1->minvistime = ep->time - ((current_width+1)/2);
1803                     s_v1->maxvistime = ep->time + ((current_width+1)/2);
1804                 }
1805                 recompute_hscrollbar();
1806                 full_redisplay = TRUE;
1807             }
1808             ep->flags |= EVENT_FLAG_SEARCHRSLT;
1809             full_redisplay = TRUE;
1810
1811             if (full_redisplay)
1812                 view1_display_when_idle();
1813
1814             return(TRUE);
1815         }
1816     }
1817     sprintf (tmpbuf, "Search for an anomalous event %ld failed...\n",
1818              s_anomalycode);
1819     message_line(tmpbuf);
1820     s_srchfail_up = TRUE;
1821     return(TRUE);
1822 }
1823
1824 /****************************************************************************
1825 * anomaly_search_callback
1826 ****************************************************************************/
1827
1828 boolean anomaly_search_callback (char *s)
1829 {
1830     ulong new_anomalycode;
1831
1832     /* No events yet?  Act like the search worked, to avoid a loop */
1833     if (g_nevents == 0)
1834         return(TRUE);
1835
1836     new_anomalycode = atol(s);
1837
1838     if (new_anomalycode == 0)
1839         return(FALSE);
1840
1841     if (new_anomalycode != s_anomalycode ||
1842         vec_len (s_v1->matches) == 0) {
1843         s_anomalycode = new_anomalycode;
1844         if (anomaly_statistics_init()) {
1845             u8 *s;
1846
1847             s = format (0, "Search for an anomalous event %ld failed...\n",
1848                         s_anomalycode);
1849             message_line ((char *) s);
1850             vec_free (s);
1851             return (TRUE);
1852         }
1853     }
1854     return(anomaly_search_internal());
1855 }
1856
1857 /****************************************************************************
1858 * anomaly_threshold_callback
1859 ****************************************************************************/
1860
1861 boolean anomaly_threshold_callback (char *s)
1862 {
1863     f64 new_threshold;
1864
1865     /* No events yet?  Act like the search worked, to avoid a loop */
1866     if (g_nevents == 0)
1867         return(TRUE);
1868
1869     new_threshold = atof (s);
1870
1871     if (new_threshold == 0.0 || new_threshold > 10.0)
1872         return(FALSE);
1873
1874     s_v1->anomaly_threshold_stddevs = new_threshold;
1875
1876     vec_reset_length (s_v1->means);
1877     vec_reset_length (s_v1->matches);
1878     vec_reset_length (s_v1->variances);
1879     vec_reset_length (s_v1->two_stddevs);
1880     return (TRUE);
1881 }
1882
1883 /****************************************************************************
1884 * event_search
1885 ****************************************************************************/
1886
1887 static void event_search (void)
1888 {
1889     modal_dialog ("Event Search: Please Enter Event Code",
1890                   "Invalid: Please Reenter Event Code", NULL,
1891                   event_search_callback);
1892 }
1893
1894 /****************************************************************************
1895 * anomaly_search
1896 ****************************************************************************/
1897
1898 static void anomaly_search (void)
1899 {
1900     modal_dialog ("Anomaly Search: Please Enter Event Code",
1901                   "Invalid: Please Reenter Event Code", NULL,
1902                   anomaly_search_callback);
1903 }
1904
1905 /****************************************************************************
1906 * anomaly_threshold
1907 ****************************************************************************/
1908
1909 static void anomaly_threshold (void)
1910 {
1911     modal_dialog ("Anomaly Threshold: Please Enter Threshold",
1912                   "Invalid: Please Reenter Threshold in Standard Deviations",
1913                   NULL, anomaly_threshold_callback);
1914 }
1915
1916 /****************************************************************************
1917 * init_track_colors
1918 ****************************************************************************/
1919 static void init_track_colors(void)
1920 {
1921     int         i;
1922     unsigned    hash;
1923     char       *label_char;
1924     unsigned    RGB[3];
1925     gboolean    dont_care[g_npids];
1926
1927     /*
1928      * If we've already allocated the colors once, then in theory we should
1929      * just be able to re-order the GCs already created to match the new track
1930      * order; the track -> color mapping doesn't currently change at runtime.
1931      * However, it's easier just to allocate everything from fresh. As a nod in
1932      * the direction of politeness towards our poor abused X server, we at
1933      * least mop up the previously allocated GCs first, although in practice
1934      * even omitting this didn't seem to cause a problem.
1935      */
1936     if (s_color != NULL ) {
1937         gdk_colormap_free_colors(gtk_widget_get_colormap(da),
1938                                  s_color, g_npids);
1939         clib_memset(s_color, 0, sizeof(GdkColor) * g_npids);
1940     } else {
1941         /*
1942          * First time through: allocate the array to hold the GCs.
1943          */
1944         s_color = g_malloc(sizeof(GdkColor) * (g_npids+1));
1945     }
1946
1947     /*
1948      * Go through and assign a color for each track.
1949      */
1950     /* Setup entry 0 in the colormap as pure red (for selection) */
1951     s_color[0] = fg_red;
1952
1953     for (i = 1; i < g_npids; i++) {
1954         /*
1955          * We compute the color from a hash of the thread name. That way we get
1956          * a distribution of different colors, and the same thread has the same
1957          * color across multiple data sets. Unfortunately, even though the
1958          * process name and thread id are invariant across data sets, the
1959          * process id isn't, so we want to exclude that from the hash. Since
1960          * the pid appears in parentheses after the process name and tid, we
1961          * can just stop at the '(' character.
1962          *
1963          * We could create a substring and use the CLIB Jenkins hash, but given
1964          * we're hashing ascii data, a suitable Bernstein hash is pretty much
1965          * just as good, and it's easiest just to compute it inline.
1966          */
1967         label_char = get_track_label(g_pids[i].pid_value);
1968         hash = 0;
1969         while (*label_char != '\0' && *label_char != '(') {
1970             hash = hash * 33 + *label_char++;
1971         }
1972         hash += hash >> 5;    /* even out the lower order bits a touch */
1973
1974         /*
1975          * OK, now we have our hash. We get the color by using the first three
1976          * bytes of the hash for the RGB values (expanded from 8 to 16 bits),
1977          * and then use the fourth byte to choose one of R, G, B and mask this
1978          * one down. This ensures the color can't be too close to white and
1979          * therefore hard to see.
1980          *
1981          * We also drop the top bit of the green, since bright green on its own
1982          * is hard to see against white. Generally we err on the side of
1983          * keeping it dark, rather than using the full spectrum of colors. This
1984          * does result in something of a preponderance of muddy colors and a
1985          * bit of a lack of cheery bright ones, but at least you can read
1986          * everything. It would be nice to do better.
1987          */
1988         RGB[0] = (hash & 0xff000000) >> 16;
1989         RGB[1] = (hash & 0x007f0000) >> 8;
1990         RGB[2] = (hash & 0x0000ff00);
1991         RGB[hash % 3] &= 0x1fff;
1992
1993         {
1994             GdkColor color = {0, RGB[0], RGB[1], RGB[2]};
1995             s_color[i] = color;
1996             g_pids[i].color_index = i;
1997         }
1998     }
1999
2000     /*
2001      * Actually allocate the colors in one bulk operation. We ignore the return
2002      * values.
2003      */
2004     gdk_colormap_alloc_colors(gtk_widget_get_colormap(da),
2005                               s_color, g_npids+1, FALSE, TRUE, dont_care);
2006 }
2007
2008
2009 /****************************************************************************
2010 * chase_event_etc
2011 * Reorder the pid_index fields so the viewer "chases" the last selected
2012 * event.
2013 ****************************************************************************/
2014
2015 static void chase_event_etc(enum chase_mode mode)
2016 {
2017     pid_sort_t *psp, *new_pidvec;
2018     pid_data_t *pp;
2019     event_t *ep;
2020     int pids_mapped;
2021     ulong code_to_chase;
2022     ulong datum_to_chase;
2023     ulong pid_to_chase;
2024     int i;
2025     int winner;
2026
2027     if (!s_last_selected_event) {
2028         infobox("No selected event",
2029                 "\nPlease select an event and try again...\n");
2030         return;
2031     }
2032
2033     /* Clear all index assignments */
2034     psp = g_pids;
2035     for (i = 0; i < g_npids; i++) {
2036         pp = psp->pid;
2037         pp->pid_index = 0xFFFFFFFF;
2038         psp++;
2039     }
2040
2041     ep = s_last_selected_event;
2042     code_to_chase = ep->code;
2043     datum_to_chase = ep->datum;
2044     pid_to_chase = ep->pid->pid_value;
2045     pids_mapped = 0;
2046     new_pidvec = g_malloc0(sizeof(pid_sort_t)*g_npids);
2047
2048     while (1) {
2049         if (srch_chase_dir == SRCH_CHASE_FORWARD) {
2050             if (ep >= g_events + g_nevents)
2051                 break;
2052         } else {
2053             if (ep < g_events)
2054                 break;
2055         }
2056
2057         winner = 0;
2058         switch(mode) {
2059         case CHASE_EVENT:
2060             if (ep->code == code_to_chase) {
2061                 winner = 1;
2062             }
2063             break;
2064
2065         case CHASE_DATUM:
2066             if (ep->datum == datum_to_chase) {
2067                 winner = 1;
2068             }
2069             break;
2070
2071         case CHASE_TRACK:
2072             if (ep->pid->pid_value == pid_to_chase) {
2073                 winner = 1;
2074             }
2075             break;
2076
2077         default:
2078             infobox("BUG", "unknown mode in chase_event_etc\n");
2079             break;
2080         }
2081
2082         if (winner) {
2083             if (ep->pid->pid_index == 0xFFFFFFFF) {
2084                 ep->pid->pid_index = pids_mapped;
2085                 new_pidvec[pids_mapped].pid = ep->pid;
2086                 new_pidvec[pids_mapped].pid_value = ep->pid->pid_value;
2087                 new_pidvec[pids_mapped].color_index = 0;
2088                 pids_mapped++;
2089                 if (pids_mapped == g_npids)
2090                     break;
2091             }
2092         }
2093         if (srch_chase_dir == SRCH_CHASE_FORWARD)
2094             ep++;
2095         else
2096             ep--;
2097     }
2098
2099     /* Pass 2, first-to-last, to collect stragglers */
2100     ep = g_events;
2101
2102     while (ep < g_events + g_nevents) {
2103         if (ep->pid->pid_index == 0xFFFFFFFF) {
2104             ep->pid->pid_index = pids_mapped;
2105             new_pidvec[pids_mapped].pid = ep->pid;
2106             new_pidvec[pids_mapped].pid_value = ep->pid->pid_value;
2107             new_pidvec[pids_mapped].color_index = 0;
2108             pids_mapped++;
2109             if (pids_mapped == g_npids)
2110                 break;
2111         }
2112         ep++;
2113     }
2114
2115     if (pids_mapped != g_npids) {
2116         infobox("BUG", "\nDidn't map all pids in chase_event_etc\n");
2117     }
2118
2119     g_free (g_pids);
2120     g_pids = new_pidvec;
2121
2122     /*
2123      * The new g_pids vector contains the "chase" sort, so we revert
2124      * the pid_index mapping to an identity map
2125      */
2126     psp = g_pids;
2127
2128     for (i = 0; i < g_npids; i++) {
2129         pp = psp->pid;
2130         pp->pid_index = i;
2131         psp++;
2132     }
2133
2134     /* AutoScroll the PID axis so we show the first "chased" event */
2135     s_v1->first_pid_index = 0;
2136     GTK_ADJUSTMENT(s_view1_vsadj)->value = 0.00;
2137     gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
2138     init_track_colors();
2139     view1_display_when_idle();
2140 }
2141
2142 /****************************************************************************
2143 * unchase_event_etc
2144 * Copy g_original_pids to g_pids, revert index mapping
2145 ****************************************************************************/
2146 static void unchase_event_etc(void)
2147 {
2148     int i;
2149     pid_sort_t *psp;
2150     pid_data_t *pp;
2151
2152     memcpy (g_pids, g_original_pids, sizeof(pid_sort_t)*g_npids);
2153
2154     /* Fix the pid structure index mappings */
2155     psp = g_pids;
2156
2157     for (i = 0; i < g_npids; i++) {
2158         pp = psp->pid;
2159         pp->pid_index = i;
2160         psp++;
2161     }
2162
2163     /* Scroll PID axis to the top */
2164     s_v1->first_pid_index = 0;
2165     GTK_ADJUSTMENT(s_view1_vsadj)->value = 0.00;
2166     gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
2167     init_track_colors();
2168     view1_display_when_idle();
2169 }
2170
2171 /****************************************************************************
2172 * print_ps_header
2173 * To fit a reasonable-sized landscape mode plot onto letter-size paper,
2174 * scale everything by .75.
2175 ****************************************************************************/
2176
2177 static void print_ps_header (v1_geometry_t *vp, char *filename)
2178 {
2179     time_t now;
2180
2181     now = time(0);
2182
2183     fprintf(s_printfp, "%%%%!PS-Adobe-3.0 EPSF-3.0\n");
2184     fprintf(s_printfp, "%%%%Creator: G2 Event Viewer\n");
2185     fprintf(s_printfp, "%%%%Title: %s\n", filename);
2186     fprintf(s_printfp, "%%%%CreationDate: %s", ctime(&now));
2187     fprintf(s_printfp, "%%%%DocumentData: Clean7Bit\n");
2188     fprintf(s_printfp, "%%%%Origin: 0 0\n");
2189     fprintf(s_printfp, "%%%%BoundingBox: 0 0 %d %d\n", vp->total_height,
2190            vp->total_width);
2191     fprintf(s_printfp, "%%%%LanguageLevel: 2\n");
2192     fprintf(s_printfp, "%%%%Pages: 1\n");
2193     fprintf(s_printfp, "%%%%Page: 1 1\n");
2194     fprintf(s_printfp, "%%%%EOF\n");
2195     fprintf(s_printfp, "/Times-Roman findfont\n");
2196     fprintf(s_printfp, "12 scalefont\n");
2197     fprintf(s_printfp, "setfont\n");
2198     fprintf(s_printfp, ".75 .75 scale\n");
2199 }
2200
2201 /****************************************************************************
2202 * xrt
2203 * Xcoordinate rotate and translate.  We need to emit postscript that
2204 * has a reasonable aspect ratio for printing.  To do that, we rotate the
2205 * intended picture by 90 degrees, using the standard 2D rotation
2206 * formula:
2207 *
2208 *     Xr = x*cos(theta) - y*sin(theta);
2209 *     Yr = x*sin(theta) + y*cos(theta);
2210 *
2211 * If we let theta = 90, this reduces to
2212 *     Xr = -y
2213 *     Yr =  x
2214 *
2215 * Translate back to the origin in X by adding Ymax, yielding
2216 *     Xrt = Ymax - y
2217 ****************************************************************************/
2218
2219 static inline int xrt(int x, int y)
2220 {
2221     return (s_v1->total_height - y);
2222 }
2223
2224 static inline int yrt(int x, int y)
2225 {
2226     return(x);
2227 }
2228
2229 /****************************************************************************
2230 * print_screen_callback
2231 ****************************************************************************/
2232
2233 static boolean print_screen_callback(char *filename)
2234 {
2235     s_printfp = fopen (filename, "wt");
2236
2237     if (s_printfp == NULL)
2238         return(FALSE);
2239
2240     /*
2241      * This variable allows us to magically turn the view1 display
2242      * code into a print-driver, with a minimum of fuss. The idea is to
2243      * magically change TBOX_DRAW_XXX into TBOX_PRINT_XXX by adding
2244      * the required value, aka s_print_offset.
2245      * Make sure to fix g2.h if you mess here, or vice versa.
2246      */
2247     s_print_offset = TBOX_PRINT_PLAIN - TBOX_DRAW_PLAIN;
2248
2249     print_ps_header(s_v1, filename);
2250
2251     display_pid_axis(s_v1);
2252     display_event_data(s_v1);
2253     display_time_axis(s_v1);
2254
2255     fclose (s_printfp);
2256     s_printfp = 0;
2257     s_print_offset = 0;
2258
2259     /* For tactile feedback */
2260     view1_display_when_idle();
2261     return(TRUE);
2262 }
2263
2264 int event_time_cmp (const void *a, const void *b)
2265 {
2266     const event_t *e1 = a;
2267     const event_t *e2 = b;
2268
2269     if (e1->time < e2->time)
2270         return -1;
2271     else if (e1->time > e2->time)
2272         return 1;
2273     return 0;
2274 }
2275
2276 /****************************************************************************
2277 * slew_tracks
2278 ****************************************************************************/
2279 static void slew_tracks (v1_geometry_t *vp, enum view1_button_click which)
2280 {
2281     event_t *ep;
2282     pid_sort_t *pp;
2283     int pid_index;
2284     ulonglong delta;
2285
2286     delta = (ulonglong) (vp->last_time_interval);
2287
2288     /* Make sure we don't push events to the left of the big bang */
2289     if (which == SLEW_LEFT_BUTTON) {
2290         for (ep = g_events; ep < (g_events + g_nevents); ep++) {
2291             pid_index = ep->pid->pid_index;
2292             pp = (g_pids + pid_index);
2293
2294             if (pp->selected) {
2295                 if (ep->time < delta) {
2296                     infobox("Slew Range Error",
2297                             "\nCan't slew selected data left that far..."
2298                             "\nEvents would preceed the Big Bang (t=0)...");
2299                     goto out;
2300                 }
2301             }
2302         }
2303     }
2304
2305     for (ep = g_events; ep < (g_events + g_nevents); ep++) {
2306         pid_index = ep->pid->pid_index;
2307         pp = (g_pids + pid_index);
2308
2309         if (pp->selected) {
2310             if (which == SLEW_LEFT_BUTTON)
2311                 ep->time -= delta;
2312             else
2313                 ep->time += delta;
2314         }
2315     }
2316
2317     /* Re-sort the events, to avoid screwing up the event display */
2318     qsort (g_events, g_nevents, sizeof(event_t), event_time_cmp);
2319
2320     /* De-select tracks */
2321     deselect_tracks();
2322
2323 out:
2324     view1_display_when_idle();
2325 }
2326
2327 /****************************************************************************
2328 * view1_button_click_callback
2329 ****************************************************************************/
2330
2331 static void view1_button_click_callback(GtkButton *item, gpointer data)
2332 {
2333     enum view1_button_click click = (enum view1_button_click) data;
2334     event_t *ep;
2335     ulonglong event_incdec;
2336     ulonglong current_width;
2337     ulonglong zoom_delta;
2338
2339     current_width = s_v1->maxvistime - s_v1->minvistime;
2340     event_incdec = (current_width) / 3;
2341
2342     if (event_incdec == 0LL)
2343         event_incdec = 1;
2344
2345     zoom_delta = (s_v1->maxvistime - s_v1->minvistime) / 6;
2346
2347     switch(click) {
2348     case TOP_BUTTON:
2349         /* First PID to top of window */
2350         s_v1->first_pid_index = 0;
2351         GTK_ADJUSTMENT(s_view1_vsadj)->value = 0.00;
2352         gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
2353         break;
2354
2355     case BOTTOM_BUTTON:
2356         s_v1->first_pid_index = g_npids - s_v1->npids;
2357         if (s_v1->first_pid_index < 0)
2358             s_v1->first_pid_index = 0;
2359         GTK_ADJUSTMENT(s_view1_vsadj)->value = (gdouble)s_v1->first_pid_index;
2360         gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
2361         break;
2362
2363     case SNAP_BUTTON:
2364         add_snapshot();
2365         break;
2366
2367     case NEXT_BUTTON:
2368         next_snapshot();
2369         break;
2370
2371     case DEL_BUTTON:
2372         del_snapshot();
2373         break;
2374
2375     case CHASE_EVENT_BUTTON:
2376         chase_event_etc(CHASE_EVENT);
2377         break;
2378
2379     case CHASE_DATUM_BUTTON:
2380         chase_event_etc(CHASE_DATUM);
2381         break;
2382
2383     case CHASE_TRACK_BUTTON:
2384         chase_event_etc(CHASE_TRACK);
2385         break;
2386
2387     case UNCHASE_BUTTON:
2388         unchase_event_etc();
2389         break;
2390
2391     case START_BUTTON:
2392     start_button:
2393         s_v1->minvistime = 0LL;
2394         s_v1->maxvistime = current_width;
2395         recompute_hscrollbar();
2396         break;
2397
2398     case ZOOMIN_BUTTON:
2399         s_v1->minvistime += zoom_delta;
2400         s_v1->maxvistime -= zoom_delta;
2401         recompute_hscrollbar();
2402         break;
2403
2404     case SEARCH_AGAIN_BUTTON:
2405         if (s_srchcode) {
2406             event_search_internal();
2407             break;
2408         }
2409         /* NOTE FALLTHROUGH */
2410
2411     case SEARCH_BUTTON:
2412         event_search();
2413         break;
2414
2415     case ANOMALY_THRESHOLD_BUTTON:
2416         anomaly_threshold();
2417         break;
2418
2419     case ANOMALY_NEXT_BUTTON:
2420         if (s_anomalycode) {
2421             anomaly_search_internal();
2422             break;
2423         }
2424         /* NOTE FALLTHROUGH */
2425
2426     case ANOMALY_BUTTON:
2427         anomaly_search();
2428         break;
2429
2430     case ZOOMOUT_BUTTON:
2431         if (zoom_delta == 0LL)
2432             zoom_delta = 1;
2433
2434         if (s_v1->minvistime >= zoom_delta) {
2435             s_v1->minvistime -= zoom_delta;
2436             s_v1->maxvistime += zoom_delta;
2437         } else {
2438             s_v1->minvistime = 0;
2439             s_v1->maxvistime += zoom_delta*2;
2440         }
2441
2442         if ((s_v1->maxvistime - s_v1->minvistime) * 8 >
2443             g_events[g_nevents-1].time * 9) {
2444             s_v1->minvistime = 0;
2445             s_v1->maxvistime = g_events[g_nevents-1].time * 9 / 8;
2446             /* Single event? Make window 1s wide... */
2447             if (g_nevents == 1)
2448                 s_v1->maxvistime = 1000000;
2449
2450         }
2451         recompute_hscrollbar();
2452         break;
2453
2454     case END_BUTTON:
2455         ep = (g_events + g_nevents - 1);
2456         s_v1->maxvistime = ep->time + event_incdec/3;
2457         s_v1->minvistime = s_v1->maxvistime - current_width;
2458         if (s_v1->minvistime > s_v1->maxvistime)
2459             goto start_button;
2460         recompute_hscrollbar();
2461         break;
2462
2463     case MORE_TRACES_BUTTON:
2464         /* Reduce the strip height to fit more traces on screen */
2465         s_v1->strip_height -= 1;
2466
2467         if (s_v1->strip_height < 1) {
2468             s_v1->strip_height = 1;
2469         }
2470
2471         /* Recalculate the number of strips on the screen */
2472         s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) /
2473             s_v1->strip_height;
2474         recompute_vscrollbar();
2475         break;
2476
2477     case LESS_TRACES_BUTTON:
2478         /* Increase the strip height to fit fewer on the screen */
2479         s_v1->strip_height += 1;
2480         if (s_v1->strip_height > 80) {
2481             s_v1->strip_height = 80;
2482         }
2483
2484         /* Recalculate the number of strips on the screen */
2485         s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) /
2486             s_v1->strip_height;
2487         recompute_vscrollbar();
2488         break;
2489
2490     case FORWARD_BUTTON:
2491         srch_chase_dir = SRCH_CHASE_FORWARD;
2492         gtk_widget_hide (s_view1_forward_button);
2493         gtk_widget_show (s_view1_backward_button);
2494         break;
2495
2496     case BACKWARD_BUTTON:
2497         srch_chase_dir = SRCH_CHASE_BACKWARD;
2498         gtk_widget_show (s_view1_forward_button);
2499         gtk_widget_hide (s_view1_backward_button);
2500         break;
2501
2502     case SUMMARY_BUTTON:
2503         summary_mode = TRUE;
2504         gtk_widget_hide (s_view1_summary_button);
2505         gtk_widget_show (s_view1_nosummary_button);
2506         break;
2507
2508     case NOSUMMARY_BUTTON:
2509         summary_mode = FALSE;
2510         gtk_widget_show (s_view1_summary_button);
2511         gtk_widget_hide (s_view1_nosummary_button);
2512         break;
2513
2514     case SLEW_LEFT_BUTTON:
2515     case SLEW_RIGHT_BUTTON:
2516         if (s_v1->last_time_interval < 10e-9) {
2517             infobox("slew", "\nNo time interval set...\n");
2518             break;
2519         }
2520         slew_tracks (s_v1, click);
2521         break;
2522     }
2523
2524     view1_display_when_idle();
2525 }
2526
2527 /****************************************************************************
2528 * view1_print_callback
2529 ****************************************************************************/
2530
2531 void view1_print_callback (GtkToggleButton *notused, gpointer nu2)
2532 {
2533     modal_dialog("Print Screen (PostScript format) to file:",
2534                  "Invalid file: Print Screen to file:",
2535                  "g2.ps", print_screen_callback);
2536 }
2537
2538 /****************************************************************************
2539 * view1_hscroll
2540 ****************************************************************************/
2541
2542 static void view1_hscroll (GtkAdjustment *adj, GtkWidget *notused)
2543 {
2544     ulonglong current_width;
2545
2546     current_width = (s_v1->maxvistime - s_v1->minvistime);
2547
2548     s_v1->minvistime = (ulonglong)(adj->value);
2549     s_v1->maxvistime = s_v1->minvistime + current_width;
2550
2551     view1_display_when_idle();
2552
2553 #ifdef NOTDEF
2554     g_print ("adj->lower = %.2f\n", adj->lower);
2555     g_print ("adj->upper = %.2f\n", adj->upper);
2556     g_print ("adj->value = %.2f\n", adj->value);
2557     g_print ("adj->step_increment = %.2f\n", adj->step_increment);
2558     g_print ("adj->page_increment = %.2f\n", adj->page_increment);
2559     g_print ("adj->page_size = %.2f\n", adj->page_size);
2560 #endif
2561 }
2562
2563 /****************************************************************************
2564 * view1_vscroll
2565 ****************************************************************************/
2566
2567 static void view1_vscroll (GtkAdjustment *adj, GtkWidget *notused)
2568 {
2569     s_v1->first_pid_index = (int)adj->value;
2570     view1_display_when_idle();
2571 }
2572
2573 void set_pid_ax_width(int width)
2574 {
2575     s_v1->pid_ax_width = width;
2576     view1_display_when_idle();
2577 }
2578
2579 /****************************************************************************
2580 * view1_init
2581 ****************************************************************************/
2582
2583 void view1_init(void)
2584 {
2585     c_view1_draw_width = atol(getprop_default("drawbox_width", "700"));
2586     c_view1_draw_height = atol(getprop_default("drawbox_height", "400"));
2587
2588     s_v1->pid_ax_width = 80;
2589     s_v1->time_ax_height = 80;
2590     s_v1->time_ax_spacing = 100;
2591     s_v1->strip_height = 25;
2592     s_v1->pop_offset = 20;
2593     s_v1->pid_ax_offset = 34;
2594     s_v1->event_offset = 40;
2595     s_v1->total_height = c_view1_draw_height;
2596     s_v1->total_width = c_view1_draw_width;
2597     s_v1->first_pid_index = 0;
2598     s_v1->anomaly_threshold_stddevs =
2599         atof(getprop_default("anomaly_threshold_stddevs", "2.5"));
2600     s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) /
2601         s_v1->strip_height;
2602
2603     s_v1->minvistime = 0;
2604     s_v1->maxvistime = 200;
2605
2606     s_view1_vbox = gtk_vbox_new(FALSE, 5);
2607
2608     s_view1_hbox = gtk_hbox_new(FALSE, 5);
2609
2610     da = gtk_drawing_area_new();
2611     gtk_drawing_area_size(GTK_DRAWING_AREA(da), c_view1_draw_width,
2612                           c_view1_draw_height);
2613
2614 #ifdef NOTDEF
2615     gtk_signal_connect (GTK_OBJECT (da), "motion_notify_event",
2616                         (GtkSignalFunc) motion_notify_event, NULL);
2617 #endif
2618
2619     gtk_signal_connect (GTK_OBJECT (da), "expose_event",
2620                         (GtkSignalFunc) expose_event, NULL);
2621
2622     gtk_signal_connect (GTK_OBJECT(da),"configure_event",
2623                         (GtkSignalFunc) configure_event, NULL);
2624
2625     gtk_signal_connect (GTK_OBJECT (da), "button_press_event",
2626                         (GtkSignalFunc) button_press_event, NULL);
2627
2628     gtk_signal_connect (GTK_OBJECT (da), "button_release_event",
2629                         (GtkSignalFunc) button_press_event, NULL);
2630
2631     gtk_signal_connect (GTK_OBJECT (da), "motion_notify_event",
2632                         (GtkSignalFunc) button_press_event, NULL);
2633
2634     gtk_widget_set_events (da, GDK_BUTTON_PRESS_MASK
2635                            | GDK_BUTTON_RELEASE_MASK | GDK_EXPOSURE_MASK
2636                            | GDK_BUTTON_MOTION_MASK);
2637
2638
2639     gtk_box_pack_start(GTK_BOX(s_view1_hbox), da, TRUE, TRUE, 0);
2640
2641     g_font = gdk_font_load ("8x13");
2642     if (g_font == NULL) {
2643         g_error("Couldn't load 8x13 font...\n");
2644     }
2645     gdk_font_ref(g_font);
2646
2647     /* PID axis menu */
2648     s_view1_vmenubox = gtk_vbox_new(FALSE, 5);
2649
2650     s_view1_vsadj = gtk_adjustment_new(0.0 /* initial value */,
2651                                        0.0 /* minimum value */,
2652                                        2000.0 /* maximum value */,
2653                                        0.1 /* step increment */,
2654                                        10.0/* page increment */,
2655                                        10.0/* page size */);
2656
2657     s_view1_vscroll = gtk_vscrollbar_new (GTK_ADJUSTMENT(s_view1_vsadj));
2658
2659     gtk_signal_connect (GTK_OBJECT (s_view1_vsadj), "value-changed",
2660                         GTK_SIGNAL_FUNC (view1_vscroll),
2661                         (gpointer)s_view1_vscroll);
2662
2663     s_view1_topbutton = gtk_button_new_with_label("Top");
2664     s_view1_bottombutton = gtk_button_new_with_label("Bottom");
2665
2666     gtk_signal_connect (GTK_OBJECT(s_view1_topbutton), "clicked",
2667                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2668                         (gpointer) TOP_BUTTON);
2669
2670     gtk_signal_connect (GTK_OBJECT(s_view1_bottombutton), "clicked",
2671                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2672                         (gpointer) BOTTOM_BUTTON);
2673
2674     /* More Traces button and Less Traces button */
2675     s_view1_more_traces_button = gtk_button_new_with_label("More Traces");
2676     s_view1_less_traces_button = gtk_button_new_with_label("Less Traces");
2677     gtk_signal_connect (GTK_OBJECT(s_view1_more_traces_button), "clicked",
2678                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2679                         (gpointer) MORE_TRACES_BUTTON);
2680     gtk_signal_connect (GTK_OBJECT(s_view1_less_traces_button), "clicked",
2681                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2682                         (gpointer) LESS_TRACES_BUTTON);
2683
2684 #ifdef NOTDEF
2685     /* Trick to bottom-justify the menu: */
2686     s_view1_pad1 = gtk_vbox_new(FALSE, 0);
2687     gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_pad1,
2688                         TRUE, FALSE, 0);
2689
2690 #endif
2691
2692     gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_topbutton,
2693                         FALSE, FALSE, 0);
2694
2695     gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_vscroll,
2696                         TRUE, TRUE, 0);
2697
2698     gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_bottombutton,
2699                         FALSE, FALSE, 0);
2700
2701     gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_more_traces_button,
2702                         FALSE, FALSE, 0);
2703
2704     gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_less_traces_button,
2705                         FALSE, FALSE, 0);
2706
2707     gtk_box_pack_start (GTK_BOX(s_view1_hbox), s_view1_vmenubox,
2708                         FALSE, FALSE, 0);
2709
2710     /* Time axis menu */
2711
2712     s_view1_hmenubox = gtk_hbox_new(FALSE, 5);
2713
2714     s_view1_startbutton = gtk_button_new_with_label("Start");
2715
2716     s_view1_zoominbutton = gtk_button_new_with_label("ZoomIn");
2717
2718     s_view1_searchbutton = gtk_button_new_with_label("Search");
2719     s_view1_srchagainbutton = gtk_button_new_with_label("Search Again");
2720
2721     s_view1_anomalybutton = gtk_button_new_with_label("Anomaly");
2722     s_view1_anomalynextbutton = gtk_button_new_with_label("Next Anomaly");
2723     s_view1_anomalythresholdbutton =
2724         gtk_button_new_with_label ("Anomaly Threshold");
2725
2726     s_view1_zoomoutbutton = gtk_button_new_with_label("ZoomOut");
2727
2728     s_view1_endbutton = gtk_button_new_with_label("End");
2729
2730     gtk_signal_connect (GTK_OBJECT(s_view1_startbutton), "clicked",
2731                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2732                         (gpointer) START_BUTTON);
2733
2734     gtk_signal_connect (GTK_OBJECT(s_view1_zoominbutton), "clicked",
2735                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2736                         (gpointer) ZOOMIN_BUTTON);
2737
2738     gtk_signal_connect (GTK_OBJECT(s_view1_searchbutton), "clicked",
2739                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2740                         (gpointer) SEARCH_BUTTON);
2741
2742     gtk_signal_connect (GTK_OBJECT(s_view1_srchagainbutton), "clicked",
2743                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2744                         (gpointer) SEARCH_AGAIN_BUTTON);
2745
2746     gtk_signal_connect (GTK_OBJECT(s_view1_anomalybutton), "clicked",
2747                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2748                         (gpointer) ANOMALY_BUTTON);
2749
2750     gtk_signal_connect (GTK_OBJECT(s_view1_anomalynextbutton), "clicked",
2751                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2752                         (gpointer) ANOMALY_NEXT_BUTTON);
2753
2754     gtk_signal_connect (GTK_OBJECT(s_view1_anomalythresholdbutton),
2755                         "clicked", GTK_SIGNAL_FUNC(view1_button_click_callback),
2756                         (gpointer) ANOMALY_THRESHOLD_BUTTON);
2757
2758     gtk_signal_connect (GTK_OBJECT(s_view1_zoomoutbutton), "clicked",
2759                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2760                         (gpointer) ZOOMOUT_BUTTON);
2761
2762     gtk_signal_connect (GTK_OBJECT(s_view1_endbutton), "clicked",
2763                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2764                         (gpointer) END_BUTTON);
2765
2766     s_view1_hsadj = gtk_adjustment_new(0.0 /* initial value */,
2767                                        0.0 /* minimum value */,
2768                                        2000.0 /* maximum value */,
2769                                        0.1 /* step increment */,
2770                                        10.0/* page increment */,
2771                                        10.0/* page size */);
2772
2773     s_view1_hscroll = gtk_hscrollbar_new (GTK_ADJUSTMENT(s_view1_hsadj));
2774
2775     gtk_signal_connect (GTK_OBJECT (s_view1_hsadj), "value-changed",
2776                         GTK_SIGNAL_FUNC (view1_hscroll),
2777                         (gpointer)s_view1_hscroll);
2778
2779     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_startbutton,
2780                         FALSE, FALSE, 0);
2781
2782     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_hscroll,
2783                         TRUE, TRUE, 0);
2784
2785     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_endbutton,
2786                         FALSE, FALSE, 0);
2787
2788     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_zoominbutton,
2789                         FALSE, FALSE, 0);
2790
2791     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_anomalybutton,
2792                         FALSE, FALSE, 0);
2793     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_anomalynextbutton,
2794                         FALSE, FALSE, 0);
2795     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_searchbutton,
2796                         FALSE, FALSE, 0);
2797
2798     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_srchagainbutton,
2799                         FALSE, FALSE, 0);
2800
2801     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_zoomoutbutton,
2802                         FALSE, FALSE, 0);
2803
2804     gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_hbox,
2805                         TRUE, TRUE, 0);
2806
2807     gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_hmenubox,
2808                         FALSE, FALSE, 0);
2809
2810
2811     s_view1_hmenubox2 = gtk_hbox_new(FALSE, 5);
2812
2813     s_view1_snapbutton = gtk_button_new_with_label("Snap");
2814
2815     s_view1_nextbutton = gtk_button_new_with_label("Next");
2816
2817     s_view1_delbutton = gtk_button_new_with_label("Del");
2818
2819     s_view1_chase_event_button = gtk_button_new_with_label("ChaseEvent");
2820
2821     s_view1_chase_datum_button = gtk_button_new_with_label("ChaseDatum");
2822
2823     s_view1_chase_track_button = gtk_button_new_with_label("ChaseTrack");
2824
2825     s_view1_unchasebutton = gtk_button_new_with_label("NoChase");
2826
2827     s_view1_forward_button = gtk_button_new_with_label("->SrchChase(is<-)");
2828     s_view1_backward_button = gtk_button_new_with_label("<-SrchChase(is->)");
2829
2830     s_view1_summary_button = gtk_button_new_with_label("Summary");
2831     s_view1_nosummary_button = gtk_button_new_with_label("NoSummary");
2832
2833     s_view1_time_slew_left_button = gtk_button_new_with_label("<-TimeSlew");
2834     s_view1_time_slew_right_button = gtk_button_new_with_label("TimeSlew->");
2835
2836     gtk_signal_connect (GTK_OBJECT(s_view1_snapbutton), "clicked",
2837                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2838                         (gpointer) SNAP_BUTTON);
2839
2840     gtk_signal_connect (GTK_OBJECT(s_view1_nextbutton), "clicked",
2841                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2842                         (gpointer) NEXT_BUTTON);
2843
2844     gtk_signal_connect (GTK_OBJECT(s_view1_delbutton), "clicked",
2845                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2846                         (gpointer) DEL_BUTTON);
2847
2848     gtk_signal_connect (GTK_OBJECT(s_view1_chase_event_button), "clicked",
2849                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2850                         (gpointer) CHASE_EVENT_BUTTON);
2851
2852     gtk_signal_connect (GTK_OBJECT(s_view1_chase_datum_button), "clicked",
2853                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2854                         (gpointer) CHASE_DATUM_BUTTON);
2855
2856     gtk_signal_connect (GTK_OBJECT(s_view1_chase_track_button), "clicked",
2857                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2858                         (gpointer) CHASE_TRACK_BUTTON);
2859
2860     gtk_signal_connect (GTK_OBJECT(s_view1_unchasebutton), "clicked",
2861                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2862                         (gpointer) UNCHASE_BUTTON);
2863
2864     gtk_signal_connect (GTK_OBJECT(s_view1_forward_button), "clicked",
2865                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2866                         (gpointer) FORWARD_BUTTON);
2867
2868     gtk_signal_connect (GTK_OBJECT(s_view1_backward_button), "clicked",
2869                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2870                         (gpointer) BACKWARD_BUTTON);
2871
2872     gtk_signal_connect (GTK_OBJECT(s_view1_summary_button), "clicked",
2873                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2874                         (gpointer) SUMMARY_BUTTON);
2875
2876     gtk_signal_connect (GTK_OBJECT(s_view1_nosummary_button), "clicked",
2877                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2878                         (gpointer) NOSUMMARY_BUTTON);
2879
2880     gtk_signal_connect (GTK_OBJECT(s_view1_time_slew_left_button), "clicked",
2881                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2882                         (gpointer) SLEW_LEFT_BUTTON);
2883
2884     gtk_signal_connect (GTK_OBJECT(s_view1_time_slew_right_button), "clicked",
2885                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2886                         (gpointer) SLEW_RIGHT_BUTTON);
2887
2888     gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_hmenubox2,
2889                         FALSE, FALSE, 0);
2890
2891     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_snapbutton,
2892                         FALSE, FALSE, 0);
2893
2894     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_nextbutton,
2895                         FALSE, FALSE, 0);
2896
2897     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_delbutton,
2898                         FALSE, FALSE, 0);
2899
2900     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_chase_event_button,
2901                         FALSE, FALSE, 0);
2902
2903     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_chase_datum_button,
2904                         FALSE, FALSE, 0);
2905
2906     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_chase_track_button,
2907                         FALSE, FALSE, 0);
2908
2909     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_unchasebutton,
2910                         FALSE, FALSE, 0);
2911
2912     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_forward_button,
2913                         FALSE, FALSE, 0);
2914
2915     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_backward_button,
2916                         FALSE, FALSE, 0);
2917
2918     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_summary_button,
2919                         FALSE, FALSE, 0);
2920
2921     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_nosummary_button,
2922                         FALSE, FALSE, 0);
2923
2924     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2),
2925                         s_view1_time_slew_left_button,
2926                         FALSE, FALSE, 0);
2927
2928     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2),
2929                         s_view1_time_slew_right_button,
2930                         FALSE, FALSE, 0);
2931
2932     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2),
2933                         s_view1_anomalythresholdbutton,
2934                         FALSE, FALSE, 0);
2935
2936     s_view1_label = gtk_label_new(NULL);
2937
2938     gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_label,
2939                         FALSE, FALSE, 0);
2940
2941     gtk_box_pack_start (GTK_BOX(g_mainhbox), s_view1_vbox,
2942                         TRUE, TRUE, 0);
2943
2944     gtk_widget_show_all (s_view1_vbox);
2945     GTK_WIDGET_SET_FLAGS(da, GTK_CAN_FOCUS);
2946     gtk_widget_grab_focus(da);
2947
2948     gtk_widget_hide (s_view1_forward_button);
2949     gtk_widget_hide (summary_mode ? s_view1_summary_button
2950                                   : s_view1_nosummary_button);
2951
2952     zi_source = gdk_bitmap_create_from_data (NULL, (char *)zi_bits, zi_width,
2953                                              zi_height);
2954     zi_mask = gdk_bitmap_create_from_data (NULL, (char *)zi_bkgd, zi_width,
2955                                            zi_height);
2956
2957     zi_cursor = (GdkCursor *) gdk_cursor_new_from_pixmap (zi_source,
2958                                                           zi_mask, &fg_black,
2959                                                           &bg_white, zi_x_hot,
2960                                                           zi_y_hot);
2961     gdk_pixmap_unref (zi_source);
2962     gdk_pixmap_unref (zi_mask);
2963
2964     norm_cursor = (GdkCursor *) gdk_cursor_new (GDK_TOP_LEFT_ARROW);
2965 }
2966
2967 /****************************************************************************
2968 * line_print
2969 ****************************************************************************/
2970
2971 void line_print (int x1, int y1, int x2, int y2)
2972 {
2973     fprintf(s_printfp, "newpath\n");
2974     fprintf(s_printfp, "%d %d moveto\n", xrt(x1, s_v1->total_height - y1),
2975             yrt(x1, s_v1->total_height - y1));
2976
2977     fprintf(s_printfp, "%d %d lineto\n", xrt (x2, s_v1->total_height - y2),
2978             yrt (x2, s_v1->total_height - y2));
2979     fprintf(s_printfp, "1 setlinewidth\n");
2980     fprintf(s_printfp, "stroke\n");
2981 }
2982
2983 /****************************************************************************
2984 * tbox_print
2985 ****************************************************************************/
2986 GdkRectangle *tbox_print (char *s, int x, int y, enum view1_tbox_fn function,
2987                           GdkRectangle *rp)
2988 {
2989     if (function == TBOX_PRINT_BOXED) {
2990         rp->width -= 4;
2991     }
2992
2993     if ((function == TBOX_PRINT_BOXED) ||
2994         (function == TBOX_PRINT_EVENT)) {
2995
2996         fprintf(s_printfp, "newpath\n");
2997         fprintf(s_printfp, "0 setlinewidth\n");
2998         fprintf(s_printfp, "%d %d moveto\n",
2999                 xrt(rp->x, s_v1->total_height - rp->y),
3000                 yrt(rp->x, s_v1->total_height - rp->y));
3001
3002         fprintf(s_printfp, "%d %d lineto\n",
3003                 xrt (rp->x+rp->width, s_v1->total_height - rp->y),
3004                 yrt (rp->x+rp->width, s_v1->total_height - rp->y));
3005
3006         fprintf(s_printfp, "%d %d lineto\n",
3007                 xrt(rp->x+rp->width, s_v1->total_height - (rp->y+rp->height)),
3008                 yrt(rp->x+rp->width, s_v1->total_height - (rp->y+rp->height)));
3009
3010         fprintf(s_printfp, "%d %d lineto\n",
3011                 xrt(rp->x, s_v1->total_height - (rp->y+rp->height)),
3012                 yrt(rp->x, s_v1->total_height - (rp->y+rp->height)));
3013
3014         fprintf(s_printfp, "%d %d lineto\n",
3015                 xrt(rp->x, s_v1->total_height - rp->y),
3016                 yrt(rp->x, s_v1->total_height - rp->y));
3017
3018         fprintf(s_printfp, "stroke\n");
3019     }
3020
3021     if ((function == TBOX_PRINT_BOXED) ||
3022         (function == TBOX_PRINT_PLAIN)) {
3023
3024         fprintf(s_printfp, "newpath\n");
3025         fprintf(s_printfp, "%d %d moveto\n",
3026                 xrt(x, s_v1->total_height - (y-2)),
3027                 yrt(x, s_v1->total_height - (y-2)));
3028         fprintf(s_printfp, "gsave\n");
3029         fprintf(s_printfp, "90 rotate\n");
3030         fprintf(s_printfp, "(%s) show\n", s);
3031         fprintf(s_printfp, "grestore\n");
3032     }
3033
3034     return(rp);
3035 }
3036
3037 /****************************************************************************
3038 * tbox - draws an optionally boxed string whose lower lefthand
3039 * corner is at (x, y).  As usual, Y is backwards.
3040 ****************************************************************************/
3041
3042 GdkRectangle *tbox (char *s, int x, int y, enum view1_tbox_fn function)
3043 {
3044     static GdkRectangle update_rect;
3045     gint lbearing, rbearing, width, ascent, descent;
3046
3047     gdk_string_extents (g_font, s,
3048                         &lbearing, &rbearing,
3049                         &width, &ascent, &descent);
3050
3051     /*
3052      * If we have enough room to display full size events, then just
3053      * use the BOXED function instead of the EVENT function.
3054      */
3055     if (s_v1->strip_height > 9) {
3056         switch (function) {
3057         case TBOX_DRAW_EVENT:    function = TBOX_DRAW_BOXED;    break;
3058         case TBOX_GETRECT_EVENT: function = TBOX_GETRECT_BOXED; break;
3059         case TBOX_PRINT_EVENT:   function = TBOX_PRINT_BOXED;   break;
3060         default:
3061             break;
3062             /* Nothing */
3063         }
3064     }
3065
3066     switch (function) {
3067     case TBOX_DRAW_BOXED:
3068         gdk_draw_rectangle (pm, da->style->white_gc, TRUE,
3069                             x, y - (ascent+descent+3), width + 2,
3070                             ascent + descent + 3);
3071
3072         gdk_draw_rectangle (pm, da->style->black_gc, FALSE,
3073                             x, y - (ascent+descent+3), width + 2,
3074                             ascent + descent + 3);
3075
3076         gdk_draw_string (pm, g_font, da->style->black_gc,
3077                          x + 1, y - 1, (const gchar *)s);
3078         /* NOTE FALLTHROUGH */
3079     case TBOX_GETRECT_BOXED:
3080         update_rect.x = x;
3081         update_rect.y = y -(ascent+descent+3);
3082         update_rect.width = width + 3;
3083         update_rect.height = ascent + descent + 4;
3084         if (function == TBOX_DRAW_BOXED)
3085             gtk_widget_draw (da, &update_rect);
3086         break;
3087
3088     case TBOX_DRAW_EVENT:
3089         /* We have a small event to draw...no text */
3090         gdk_draw_rectangle (pm, da->style->black_gc, FALSE,
3091                             x, y - 1, 3, 3);
3092         /* NOTE FALLTHROUGH */
3093     case TBOX_GETRECT_EVENT:
3094         update_rect.x = x;
3095         update_rect.y = y - 1;
3096         update_rect.width = 4;
3097         update_rect.height = 4;
3098         if (function == TBOX_DRAW_EVENT)
3099             gtk_widget_draw (da, &update_rect);
3100         break;
3101
3102
3103     case TBOX_DRAW_PLAIN:
3104
3105         gdk_draw_string (pm, g_font, da->style->black_gc,
3106                          x + 1, y - 1, (const gchar *)s);
3107         /* NOTE FALLTHROUGH */
3108     case TBOX_GETRECT_PLAIN:
3109         update_rect.x = x;
3110         update_rect.y = y -(ascent+descent+1);
3111         update_rect.width = width;
3112         update_rect.height = ascent + descent;
3113         if (function == TBOX_DRAW_PLAIN)
3114             gtk_widget_draw (da, &update_rect);
3115         break;
3116
3117     case TBOX_PRINT_BOXED:
3118         update_rect.x = x;
3119         update_rect.y = y -(ascent+descent+3);
3120         update_rect.width = width + 3;
3121         update_rect.height = ascent + descent + 4;
3122         /* note fallthrough */
3123     case TBOX_PRINT_PLAIN:
3124         return(tbox_print(s, x, y, function, &update_rect));
3125
3126     case TBOX_PRINT_EVENT:
3127         /* We have a small event box to print...no text */
3128         update_rect.x = x;
3129         update_rect.y = y - 1;
3130         update_rect.width = 4;
3131         update_rect.height = 4;
3132         return(tbox_print(s, x, y, function, &update_rect));
3133     }
3134     return(&update_rect);
3135 }
3136
3137 /****************************************************************************
3138 * line
3139 *
3140 * For lines there is a primitive batching facility, that doesn't update
3141 * the drawing area until the batch is complete. This is handy for drawing
3142 * the pid axis and for summary mode.
3143 *
3144 * line_batch_mode contains the state for this:
3145 *
3146 *   BATCH_OFF:      no batching, update for every line
3147 *   BATCH_NEW:      just entered a batch, so initialize the area to update from
3148 *                   scratch
3149 *   BATCH_EXISTING: have drawn at least one line in batch mode, so the update
3150 *                   area should only be expanded from now on to include the
3151 *                   union of the "rectangular hull" of all lines
3152 ****************************************************************************/
3153
3154 static enum { BATCH_OFF, BATCH_NEW, BATCH_EXISTING } line_batch_mode;
3155 static int line_batch_count;
3156 static int line_minx, line_miny, line_maxx, line_maxy;
3157
3158 void line_batch_start (void)
3159 {
3160     line_batch_mode = BATCH_NEW;
3161     line_batch_count = 0;
3162 }
3163
3164 void line_batch_end (void)
3165 {
3166     GdkRectangle update_rect;
3167     if (line_batch_count > 0) {
3168         update_rect.x = line_minx;
3169         update_rect.y = line_miny;
3170         update_rect.width = (line_maxx - line_minx) + 1;
3171         update_rect.height = (line_maxy - line_miny) + 1;
3172         gtk_widget_draw (da, &update_rect);
3173     }
3174     line_batch_mode = BATCH_OFF;
3175 }
3176
3177 void line (int x1, int y1, int x2, int y2, enum view1_line_fn function)
3178 {
3179     GdkRectangle update_rect;
3180     GdkGC *gc = NULL;
3181
3182     switch(function) {
3183     case LINE_DRAW_BLACK:
3184         gc = da->style->black_gc;
3185         break;
3186
3187     case LINE_DRAW_WHITE:
3188         gc = da->style->white_gc;
3189         break;
3190
3191     case LINE_PRINT:
3192         line_print (x1, y1, x2, y2);
3193         return;
3194     }
3195
3196     gdk_draw_line (pm, gc, x1, y1, x2, y2);
3197
3198     switch (line_batch_mode) {
3199         case BATCH_OFF:
3200             update_rect.x = x1;
3201             update_rect.y = y1;
3202             update_rect.width = (x2-x1) + 1;
3203             update_rect.height = (y2-y1) + 1;
3204             gtk_widget_draw (da, &update_rect);
3205             break;
3206
3207         case BATCH_NEW:
3208             line_minx = x1;
3209             line_maxx = x2;
3210             line_miny = y1;
3211             line_maxy = y2;
3212             line_batch_mode = BATCH_EXISTING;
3213             line_batch_count = 1;
3214             break;
3215
3216         case BATCH_EXISTING:
3217             if (line_minx > x1)
3218                 line_minx = x1;
3219             if (line_miny > y1)
3220                 line_miny = y1;
3221             if (line_maxx < x2)
3222                 line_maxx = x2;
3223             if (line_maxy < y2)
3224                 line_maxy = y2;
3225             line_batch_count++;
3226             break;
3227     }
3228 }
3229
3230
3231 /****************************************************************************
3232 * display_pid_axis
3233 ****************************************************************************/
3234
3235 static void display_pid_axis(v1_geometry_t *vp)
3236 {
3237     int y, i, label_tick;
3238     int last_printed_y = -vp->strip_height;
3239     pid_sort_t *pp;
3240     int pid_index;
3241     char *label_fmt;
3242     char tmpbuf [128];
3243
3244     /* No pids yet? Outta here */
3245     if (g_pids == NULL)
3246         return;
3247
3248     line_batch_start();
3249
3250     for (i = 0; i < vp->npids; i++) {
3251         pid_index = vp->first_pid_index + i;
3252         if (pid_index >= g_npids)
3253             break;
3254
3255         pp = (g_pids + pid_index);
3256
3257         set_color(pid_index);
3258
3259         label_fmt = get_track_label(pp->pid_value);
3260         snprintf(tmpbuf, sizeof(tmpbuf)-1, label_fmt, pp->pid_value);
3261
3262         y = i*vp->strip_height + vp->pid_ax_offset;
3263
3264         /*
3265          * Have we incremented enough space to have another label not
3266          * overlap the previous label?
3267          */
3268         if (y - last_printed_y > 9) {
3269             /* Draw label */
3270             tbox(tmpbuf, 0, y +4, TBOX_DRAW_PLAIN+s_print_offset);
3271
3272             last_printed_y = y;
3273
3274             /*
3275              * And let the line stick out a bit more to indicate this label
3276              * relates to the following line.
3277              */
3278             label_tick = 4;
3279         }
3280         else {
3281             label_tick = 0;
3282         }
3283
3284         /* Draw axis line, but only if the lines aren't too close together */
3285         if (vp->strip_height > 4) {
3286             line(vp->pid_ax_width - label_tick, y+4*s_print_offset,
3287                  vp->total_width, y+4*s_print_offset,
3288                  LINE_DRAW_BLACK+s_print_offset);
3289         }
3290     }
3291
3292     set_color(COLOR_DEFAULT);
3293     line_batch_end();
3294 }
3295
3296 /****************************************************************************
3297 * view1_read_events_callback
3298 * New event data just showed up, reset a few things.
3299 ****************************************************************************/
3300
3301 void view1_read_events_callback(void)
3302 {
3303     int max_vis_index;
3304
3305     s_v1->first_pid_index = 0;
3306
3307     max_vis_index = 300;
3308     if (max_vis_index > g_nevents)
3309         max_vis_index = g_nevents-1;
3310
3311     s_v1->minvistime = 0LL;
3312     s_v1->maxvistime = (g_events[g_nevents - 1].time * 9)/ 8;
3313     /* Single event? Make the initial display 1s wide */
3314     if (g_nevents == 1)
3315         s_v1->maxvistime = 1000000;
3316     s_srchindex = 0;
3317     s_srchcode = 0;
3318     s_last_selected_event = 0;
3319
3320     init_track_colors();
3321
3322     recompute_hscrollbar();
3323     recompute_vscrollbar();
3324 }
3325
3326 /****************************************************************************
3327 * display_event_data
3328 ****************************************************************************/
3329
3330 static void display_event_data(v1_geometry_t *vp)
3331 {
3332     int start_index;
3333     int pid_index;
3334     int x, y;
3335     event_t *ep;
3336     event_def_t *edp;
3337     double time_per_pixel;
3338     char tmpbuf[1024];
3339     GdkRectangle *print_rect;
3340     int *last_x_used;
3341
3342     /* Happens if one loads the event def header first, for example. */
3343     if (g_nevents == 0)
3344         return;
3345
3346     time_per_pixel = dtime_per_pixel(vp);
3347
3348     start_index = find_event_index (vp->minvistime);
3349
3350     /* Scrolled too far right? */
3351     if (start_index >= g_nevents)
3352         return;
3353
3354     ep = (g_events + start_index);
3355
3356     if (s_print_offset || summary_mode) {
3357         last_x_used = (int *)g_malloc0(vp->npids * sizeof(int));
3358     } else {
3359         last_x_used = NULL;
3360     }
3361
3362     line_batch_start();
3363
3364     while (ep < (g_events + g_nevents) &&
3365            (ep->time < vp->maxvistime)) {
3366         pid_index = ep->pid->pid_index;
3367         set_color(pid_index);
3368
3369         /* First filter: pid out of range */
3370         if ((pid_index < vp->first_pid_index) ||
3371             (pid_index >= vp->first_pid_index + vp->npids)) {
3372             ep++;
3373             continue;
3374         }
3375
3376         /* Second filter: event hidden */
3377         edp = find_event_definition (ep->code);
3378         if (!edp->selected) {
3379             ep++;
3380             continue;
3381         }
3382
3383         /* Display it... */
3384
3385         pid_index -= vp->first_pid_index;
3386
3387         y = pid_index*vp->strip_height + vp->event_offset;
3388
3389         x = vp->pid_ax_width +
3390             (int)(((double)(ep->time - vp->minvistime)) / time_per_pixel);
3391
3392         if (last_x_used != NULL && x < last_x_used[pid_index]) {
3393             ep++;
3394             continue;
3395         }
3396
3397         if (ep->flags & (EVENT_FLAG_SELECT | EVENT_FLAG_SEARCHRSLT)) {
3398             if (ep->flags & EVENT_FLAG_SELECT) {
3399                 format_popbox_string(tmpbuf, sizeof(tmpbuf), ep, edp);
3400 #ifdef NOTDEF
3401                 sprintf(tmpbuf, edp->name);
3402                 sprintf(tmpbuf+strlen(tmpbuf), ": ");
3403                 sprintf(tmpbuf+strlen(tmpbuf), edp->format, ep->datum);
3404 #endif
3405             } else {
3406                 sprintf(tmpbuf, "SEARCH RESULT");
3407             }
3408             print_rect = tbox(tmpbuf, x, y - vp->pop_offset,
3409                               TBOX_DRAW_BOXED+s_print_offset);
3410             line(x, y-vp->pop_offset, x, y, LINE_DRAW_BLACK+s_print_offset);
3411             if (last_x_used != NULL)
3412                 last_x_used[pid_index] = x + print_rect->width;
3413         }
3414         if (summary_mode) {
3415             int delta = vp->strip_height / 3;
3416             if (delta < 1)
3417                 delta = 1;
3418             y = pid_index*vp->strip_height + vp->pid_ax_offset;
3419             line(x, y - delta, x, y + delta, LINE_DRAW_BLACK);
3420             last_x_used[pid_index] = x + 1;
3421         } else {
3422             sprintf(tmpbuf, "%ld", ep->code);
3423             print_rect = tbox(tmpbuf, x, y, TBOX_DRAW_EVENT+s_print_offset);
3424             if (last_x_used != NULL)
3425                 last_x_used[pid_index] = x + print_rect->width;
3426         }
3427
3428         ep++;
3429     }
3430     if (last_x_used)
3431         g_free(last_x_used);
3432     line_batch_end();
3433     set_color(COLOR_DEFAULT);
3434 }
3435
3436 /****************************************************************************
3437 * display_clear
3438 ****************************************************************************/
3439
3440 static void display_clear(void)
3441 {
3442     GdkRectangle update_rect;
3443
3444     gdk_draw_rectangle (pm, da->style->white_gc, TRUE,
3445                         0, 0, da->allocation.width,
3446                         da->allocation.height);
3447
3448     update_rect.x = 0;
3449     update_rect.y = 0;
3450     update_rect.width = da->allocation.width;
3451     update_rect.height = da->allocation.height;
3452
3453     gtk_widget_draw (da, &update_rect);
3454 }
3455
3456 /****************************************************************************
3457 * display_time_axis
3458 ****************************************************************************/
3459
3460 static void display_time_axis(v1_geometry_t *vp)
3461 {
3462     int x, y, i;
3463     int xoffset, nticks;
3464     char tmpbuf [128];
3465     double unit_divisor;
3466     double time;
3467     char *units;
3468     double time_per_pixel;
3469
3470     y = vp->npids * vp->strip_height + vp->pid_ax_offset;
3471
3472     x = vp->pid_ax_width;
3473
3474     nticks = (vp->total_width - vp->pid_ax_width) / vp->time_ax_spacing;
3475
3476     time_per_pixel = dtime_per_pixel(vp);
3477
3478     units = "ns";
3479     unit_divisor = 1.00;
3480
3481     if ((vp->maxvistime / unit_divisor) > 1000) {
3482         units = "us";
3483         unit_divisor = 1000.00;
3484     }
3485
3486     if ((vp->maxvistime / unit_divisor) > 1000) {
3487         units = "ms";
3488         unit_divisor = 1000.00*1000.00;
3489     }
3490     if ((vp->maxvistime / unit_divisor) > 1000) {
3491         units = "s";
3492         unit_divisor = 1000.00*1000.00*1000.00;
3493     }
3494
3495     /* Draw line */
3496     line(x, y, vp->total_width, y, LINE_DRAW_BLACK+s_print_offset);
3497
3498     xoffset = 0;
3499
3500     for (i = 0; i < nticks; i++) {
3501         /* Tick mark */
3502         line(x+xoffset, y-3, x+xoffset, y+3, LINE_DRAW_BLACK+s_print_offset);
3503
3504         time = (double)(x + xoffset - vp->pid_ax_width);
3505         time *= time_per_pixel;
3506         time += (double)(vp->minvistime);
3507         time /= unit_divisor;
3508
3509         sprintf (tmpbuf, "%.2f%s", time, units);
3510
3511         tbox(tmpbuf, x+xoffset, y+15, TBOX_DRAW_PLAIN+s_print_offset);
3512
3513         xoffset += vp->time_ax_spacing;
3514     }
3515 }
3516
3517 /****************************************************************************
3518 * clear_scoreboard
3519 * Forget about any temporary displays, they're gone now...
3520 ****************************************************************************/
3521
3522 static void clear_scoreboard(void)
3523 {
3524     s_result_up = FALSE;
3525 }
3526
3527 /****************************************************************************
3528 * view1_display
3529 ****************************************************************************/
3530
3531 void view1_display(void)
3532 {
3533     display_clear();
3534     display_pid_axis(s_v1);
3535     display_event_data(s_v1);
3536     display_time_axis(s_v1);
3537     clear_scoreboard();
3538 }
3539
3540 static gint idle_tag;
3541
3542 /****************************************************************************
3543 * view1_display_eventually
3544 ****************************************************************************/
3545
3546 static void view1_display_eventually(void)
3547 {
3548     gtk_idle_remove(idle_tag);
3549     idle_tag = 0;
3550     view1_display();
3551 }
3552
3553
3554 /****************************************************************************
3555 * view1_display_when_idle
3556 ****************************************************************************/
3557
3558 void view1_display_when_idle(void)
3559 {
3560     if (idle_tag == 0) {
3561         idle_tag = gtk_idle_add((GtkFunction) view1_display_eventually, 0);
3562     }
3563 }
3564
3565 /****************************************************************************
3566 * view1_about
3567 ****************************************************************************/
3568
3569 void view1_about (char *tmpbuf)
3570 {
3571     int nsnaps;
3572     snapshot_t *snaps;
3573
3574     sprintf(tmpbuf+strlen(tmpbuf), "Minvistime %lld\nMaxvistime %lld\n",
3575             s_v1->minvistime, s_v1->maxvistime);
3576     sprintf(tmpbuf+strlen(tmpbuf), "Strip Height %d\n",
3577             s_v1->strip_height);
3578
3579     for (nsnaps = 0, snaps = s_snapshots; snaps; snaps = snaps->next) {
3580         nsnaps++;
3581     }
3582     sprintf(tmpbuf+strlen(tmpbuf), "%d snapshots in the ring\n", nsnaps);
3583 }