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