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