fib: fix ip drop path crashes
[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 placeholder;
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, &placeholder)) {
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, &placeholder)) {
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) (long int) 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     {
2348     case TOP_BUTTON:
2349         /* First PID to top of window */
2350         s_v1->first_pid_index = 0;
2351         GTK_ADJUSTMENT(s_view1_vsadj)->value = 0.00;
2352         gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
2353         break;
2354
2355     case BOTTOM_BUTTON:
2356         s_v1->first_pid_index = g_npids - s_v1->npids;
2357         if (s_v1->first_pid_index < 0)
2358             s_v1->first_pid_index = 0;
2359         GTK_ADJUSTMENT(s_view1_vsadj)->value = (gdouble)s_v1->first_pid_index;
2360         gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
2361         break;
2362
2363     case SNAP_BUTTON:
2364         add_snapshot();
2365         break;
2366
2367     case NEXT_BUTTON:
2368         next_snapshot();
2369         break;
2370
2371     case DEL_BUTTON:
2372         del_snapshot();
2373         break;
2374
2375     case CHASE_EVENT_BUTTON:
2376         chase_event_etc(CHASE_EVENT);
2377         break;
2378
2379     case CHASE_DATUM_BUTTON:
2380         chase_event_etc(CHASE_DATUM);
2381         break;
2382
2383     case CHASE_TRACK_BUTTON:
2384         chase_event_etc(CHASE_TRACK);
2385         break;
2386
2387     case UNCHASE_BUTTON:
2388         unchase_event_etc();
2389         break;
2390
2391     case START_BUTTON:
2392     start_button:
2393         s_v1->minvistime = 0LL;
2394         s_v1->maxvistime = current_width;
2395         recompute_hscrollbar();
2396         break;
2397
2398     case ZOOMIN_BUTTON:
2399         s_v1->minvistime += zoom_delta;
2400         s_v1->maxvistime -= zoom_delta;
2401         recompute_hscrollbar();
2402         break;
2403
2404     case SEARCH_AGAIN_BUTTON:
2405         if (s_srchcode) {
2406             event_search_internal();
2407             break;
2408         }
2409         /* NOTE FALLTHROUGH */
2410
2411     case SEARCH_BUTTON:
2412         event_search();
2413         break;
2414
2415     case ANOMALY_THRESHOLD_BUTTON:
2416         anomaly_threshold();
2417         break;
2418
2419     case ANOMALY_NEXT_BUTTON:
2420         if (s_anomalycode) {
2421             anomaly_search_internal();
2422             break;
2423         }
2424         /* NOTE FALLTHROUGH */
2425
2426     case ANOMALY_BUTTON:
2427         anomaly_search();
2428         break;
2429
2430     case ZOOMOUT_BUTTON:
2431         if (zoom_delta == 0LL)
2432             zoom_delta = 1;
2433
2434         if (s_v1->minvistime >= zoom_delta) {
2435             s_v1->minvistime -= zoom_delta;
2436             s_v1->maxvistime += zoom_delta;
2437         } else {
2438             s_v1->minvistime = 0;
2439             s_v1->maxvistime += zoom_delta*2;
2440         }
2441
2442         if ((s_v1->maxvistime - s_v1->minvistime) * 8 >
2443             g_events[g_nevents-1].time * 9) {
2444             s_v1->minvistime = 0;
2445             s_v1->maxvistime = g_events[g_nevents-1].time * 9 / 8;
2446             /* Single event? Make window 1s wide... */
2447             if (g_nevents == 1)
2448                 s_v1->maxvistime = 1000000;
2449
2450         }
2451         recompute_hscrollbar();
2452         break;
2453
2454     case END_BUTTON:
2455         ep = (g_events + g_nevents - 1);
2456         s_v1->maxvistime = ep->time + event_incdec/3;
2457         s_v1->minvistime = s_v1->maxvistime - current_width;
2458         if (s_v1->minvistime > s_v1->maxvistime)
2459             goto start_button;
2460         recompute_hscrollbar();
2461         break;
2462
2463     case MORE_TRACES_BUTTON:
2464         /* Reduce the strip height to fit more traces on screen */
2465         s_v1->strip_height -= 1;
2466
2467         if (s_v1->strip_height < 1) {
2468             s_v1->strip_height = 1;
2469         }
2470
2471         /* Recalculate the number of strips on the screen */
2472         s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) /
2473             s_v1->strip_height;
2474         recompute_vscrollbar();
2475         break;
2476
2477     case LESS_TRACES_BUTTON:
2478         /* Increase the strip height to fit fewer on the screen */
2479         s_v1->strip_height += 1;
2480         if (s_v1->strip_height > 80) {
2481             s_v1->strip_height = 80;
2482         }
2483
2484         /* Recalculate the number of strips on the screen */
2485         s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) /
2486             s_v1->strip_height;
2487         recompute_vscrollbar();
2488         break;
2489
2490     case FORWARD_BUTTON:
2491         srch_chase_dir = SRCH_CHASE_FORWARD;
2492         gtk_widget_hide (s_view1_forward_button);
2493         gtk_widget_show (s_view1_backward_button);
2494         break;
2495
2496     case BACKWARD_BUTTON:
2497         srch_chase_dir = SRCH_CHASE_BACKWARD;
2498         gtk_widget_show (s_view1_forward_button);
2499         gtk_widget_hide (s_view1_backward_button);
2500         break;
2501
2502     case SUMMARY_BUTTON:
2503         summary_mode = TRUE;
2504         gtk_widget_hide (s_view1_summary_button);
2505         gtk_widget_show (s_view1_nosummary_button);
2506         break;
2507
2508     case NOSUMMARY_BUTTON:
2509         summary_mode = FALSE;
2510         gtk_widget_show (s_view1_summary_button);
2511         gtk_widget_hide (s_view1_nosummary_button);
2512         break;
2513
2514     case SLEW_LEFT_BUTTON:
2515     case SLEW_RIGHT_BUTTON:
2516         if (s_v1->last_time_interval < 10e-9) {
2517             infobox("slew", "\nNo time interval set...\n");
2518             break;
2519         }
2520         slew_tracks (s_v1, click);
2521         break;
2522     }
2523
2524     view1_display_when_idle();
2525 }
2526
2527 /****************************************************************************
2528 * view1_print_callback
2529 ****************************************************************************/
2530
2531 void view1_print_callback (GtkToggleButton *notused, gpointer nu2)
2532 {
2533     modal_dialog("Print Screen (PostScript format) to file:",
2534                  "Invalid file: Print Screen to file:",
2535                  "g2.ps", print_screen_callback);
2536 }
2537
2538 /****************************************************************************
2539 * view1_hscroll
2540 ****************************************************************************/
2541
2542 static void view1_hscroll (GtkAdjustment *adj, GtkWidget *notused)
2543 {
2544     ulonglong current_width;
2545
2546     current_width = (s_v1->maxvistime - s_v1->minvistime);
2547
2548     s_v1->minvistime = (ulonglong)(adj->value);
2549     s_v1->maxvistime = s_v1->minvistime + current_width;
2550
2551     view1_display_when_idle();
2552
2553 #ifdef NOTDEF
2554     g_print ("adj->lower = %.2f\n", adj->lower);
2555     g_print ("adj->upper = %.2f\n", adj->upper);
2556     g_print ("adj->value = %.2f\n", adj->value);
2557     g_print ("adj->step_increment = %.2f\n", adj->step_increment);
2558     g_print ("adj->page_increment = %.2f\n", adj->page_increment);
2559     g_print ("adj->page_size = %.2f\n", adj->page_size);
2560 #endif
2561 }
2562
2563 /****************************************************************************
2564 * view1_vscroll
2565 ****************************************************************************/
2566
2567 static void view1_vscroll (GtkAdjustment *adj, GtkWidget *notused)
2568 {
2569     s_v1->first_pid_index = (int)adj->value;
2570     view1_display_when_idle();
2571 }
2572
2573 void set_pid_ax_width(int width)
2574 {
2575     s_v1->pid_ax_width = width;
2576     view1_display_when_idle();
2577 }
2578
2579 /****************************************************************************
2580 * view1_init
2581 ****************************************************************************/
2582
2583 void view1_init(void)
2584 {
2585     c_view1_draw_width = atol(getprop_default("drawbox_width", "700"));
2586     c_view1_draw_height = atol(getprop_default("drawbox_height", "400"));
2587
2588     s_v1->pid_ax_width = 80;
2589     s_v1->time_ax_height = 80;
2590     s_v1->time_ax_spacing = 100;
2591     s_v1->strip_height = 25;
2592     s_v1->pop_offset = 20;
2593     s_v1->pid_ax_offset = 34;
2594     s_v1->event_offset = 40;
2595     s_v1->total_height = c_view1_draw_height;
2596     s_v1->total_width = c_view1_draw_width;
2597     s_v1->first_pid_index = 0;
2598     s_v1->anomaly_threshold_stddevs =
2599         atof(getprop_default("anomaly_threshold_stddevs", "2.5"));
2600     s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) /
2601         s_v1->strip_height;
2602
2603     s_v1->minvistime = 0;
2604     s_v1->maxvistime = 200;
2605
2606     s_view1_vbox = gtk_vbox_new(FALSE, 5);
2607
2608     s_view1_hbox = gtk_hbox_new(FALSE, 5);
2609
2610     da = gtk_drawing_area_new();
2611     gtk_drawing_area_size(GTK_DRAWING_AREA(da), c_view1_draw_width,
2612                           c_view1_draw_height);
2613
2614 #ifdef NOTDEF
2615     gtk_signal_connect (GTK_OBJECT (da), "motion_notify_event",
2616                         (GtkSignalFunc) motion_notify_event, NULL);
2617 #endif
2618
2619     gtk_signal_connect (GTK_OBJECT (da), "expose_event",
2620                         (GtkSignalFunc) expose_event, NULL);
2621
2622     gtk_signal_connect (GTK_OBJECT(da),"configure_event",
2623                         (GtkSignalFunc) configure_event, NULL);
2624
2625     gtk_signal_connect (GTK_OBJECT (da), "button_press_event",
2626                         (GtkSignalFunc) button_press_event, NULL);
2627
2628     gtk_signal_connect (GTK_OBJECT (da), "button_release_event",
2629                         (GtkSignalFunc) button_press_event, NULL);
2630
2631     gtk_signal_connect (GTK_OBJECT (da), "motion_notify_event",
2632                         (GtkSignalFunc) button_press_event, NULL);
2633
2634     gtk_widget_set_events (da, GDK_BUTTON_PRESS_MASK
2635                            | GDK_BUTTON_RELEASE_MASK | GDK_EXPOSURE_MASK
2636                            | GDK_BUTTON_MOTION_MASK);
2637
2638
2639     gtk_box_pack_start(GTK_BOX(s_view1_hbox), da, TRUE, TRUE, 0);
2640
2641     g_font = gdk_font_load ("8x13");
2642     if (g_font == NULL) {
2643         g_error("Couldn't load 8x13 font...\n");
2644     }
2645     gdk_font_ref(g_font);
2646
2647     /* PID axis menu */
2648     s_view1_vmenubox = gtk_vbox_new(FALSE, 5);
2649
2650     s_view1_vsadj = gtk_adjustment_new(0.0 /* initial value */,
2651                                        0.0 /* minimum value */,
2652                                        2000.0 /* maximum value */,
2653                                        0.1 /* step increment */,
2654                                        10.0/* page increment */,
2655                                        10.0/* page size */);
2656
2657     s_view1_vscroll = gtk_vscrollbar_new (GTK_ADJUSTMENT(s_view1_vsadj));
2658
2659     gtk_signal_connect (GTK_OBJECT (s_view1_vsadj), "value-changed",
2660                         GTK_SIGNAL_FUNC (view1_vscroll),
2661                         (gpointer)s_view1_vscroll);
2662
2663     s_view1_topbutton = gtk_button_new_with_label("Top");
2664     s_view1_bottombutton = gtk_button_new_with_label("Bottom");
2665
2666     gtk_signal_connect (GTK_OBJECT(s_view1_topbutton), "clicked",
2667                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2668                         (gpointer) TOP_BUTTON);
2669
2670     gtk_signal_connect (GTK_OBJECT(s_view1_bottombutton), "clicked",
2671                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2672                         (gpointer) BOTTOM_BUTTON);
2673
2674     /* More Traces button and Less Traces button */
2675     s_view1_more_traces_button = gtk_button_new_with_label("More Traces");
2676     s_view1_less_traces_button = gtk_button_new_with_label("Less Traces");
2677     gtk_signal_connect (GTK_OBJECT(s_view1_more_traces_button), "clicked",
2678                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2679                         (gpointer) MORE_TRACES_BUTTON);
2680     gtk_signal_connect (GTK_OBJECT(s_view1_less_traces_button), "clicked",
2681                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2682                         (gpointer) LESS_TRACES_BUTTON);
2683
2684 #ifdef NOTDEF
2685     /* Trick to bottom-justify the menu: */
2686     s_view1_pad1 = gtk_vbox_new(FALSE, 0);
2687     gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_pad1,
2688                         TRUE, FALSE, 0);
2689
2690 #endif
2691
2692     gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_topbutton,
2693                         FALSE, FALSE, 0);
2694
2695     gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_vscroll,
2696                         TRUE, TRUE, 0);
2697
2698     gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_bottombutton,
2699                         FALSE, FALSE, 0);
2700
2701     gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_more_traces_button,
2702                         FALSE, FALSE, 0);
2703
2704     gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_less_traces_button,
2705                         FALSE, FALSE, 0);
2706
2707     gtk_box_pack_start (GTK_BOX(s_view1_hbox), s_view1_vmenubox,
2708                         FALSE, FALSE, 0);
2709
2710     /* Time axis menu */
2711
2712     s_view1_hmenubox = gtk_hbox_new(FALSE, 5);
2713
2714     s_view1_startbutton = gtk_button_new_with_label("Start");
2715
2716     s_view1_zoominbutton = gtk_button_new_with_label("ZoomIn");
2717
2718     s_view1_searchbutton = gtk_button_new_with_label("Search");
2719     s_view1_srchagainbutton = gtk_button_new_with_label("Search Again");
2720
2721     s_view1_anomalybutton = gtk_button_new_with_label("Anomaly");
2722     s_view1_anomalynextbutton = gtk_button_new_with_label("Next Anomaly");
2723     s_view1_anomalythresholdbutton =
2724         gtk_button_new_with_label ("Anomaly Threshold");
2725
2726     s_view1_zoomoutbutton = gtk_button_new_with_label("ZoomOut");
2727
2728     s_view1_endbutton = gtk_button_new_with_label("End");
2729
2730     gtk_signal_connect (GTK_OBJECT(s_view1_startbutton), "clicked",
2731                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2732                         (gpointer) START_BUTTON);
2733
2734     gtk_signal_connect (GTK_OBJECT(s_view1_zoominbutton), "clicked",
2735                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2736                         (gpointer) ZOOMIN_BUTTON);
2737
2738     gtk_signal_connect (GTK_OBJECT(s_view1_searchbutton), "clicked",
2739                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2740                         (gpointer) SEARCH_BUTTON);
2741
2742     gtk_signal_connect (GTK_OBJECT(s_view1_srchagainbutton), "clicked",
2743                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2744                         (gpointer) SEARCH_AGAIN_BUTTON);
2745
2746     gtk_signal_connect (GTK_OBJECT(s_view1_anomalybutton), "clicked",
2747                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2748                         (gpointer) ANOMALY_BUTTON);
2749
2750     gtk_signal_connect (GTK_OBJECT(s_view1_anomalynextbutton), "clicked",
2751                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2752                         (gpointer) ANOMALY_NEXT_BUTTON);
2753
2754     gtk_signal_connect (GTK_OBJECT(s_view1_anomalythresholdbutton),
2755                         "clicked", GTK_SIGNAL_FUNC(view1_button_click_callback),
2756                         (gpointer) ANOMALY_THRESHOLD_BUTTON);
2757
2758     gtk_signal_connect (GTK_OBJECT(s_view1_zoomoutbutton), "clicked",
2759                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2760                         (gpointer) ZOOMOUT_BUTTON);
2761
2762     gtk_signal_connect (GTK_OBJECT(s_view1_endbutton), "clicked",
2763                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2764                         (gpointer) END_BUTTON);
2765
2766     s_view1_hsadj = gtk_adjustment_new(0.0 /* initial value */,
2767                                        0.0 /* minimum value */,
2768                                        2000.0 /* maximum value */,
2769                                        0.1 /* step increment */,
2770                                        10.0/* page increment */,
2771                                        10.0/* page size */);
2772
2773     s_view1_hscroll = gtk_hscrollbar_new (GTK_ADJUSTMENT(s_view1_hsadj));
2774
2775     gtk_signal_connect (GTK_OBJECT (s_view1_hsadj), "value-changed",
2776                         GTK_SIGNAL_FUNC (view1_hscroll),
2777                         (gpointer)s_view1_hscroll);
2778
2779     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_startbutton,
2780                         FALSE, FALSE, 0);
2781
2782     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_hscroll,
2783                         TRUE, TRUE, 0);
2784
2785     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_endbutton,
2786                         FALSE, FALSE, 0);
2787
2788     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_zoominbutton,
2789                         FALSE, FALSE, 0);
2790
2791     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_anomalybutton,
2792                         FALSE, FALSE, 0);
2793     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_anomalynextbutton,
2794                         FALSE, FALSE, 0);
2795     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_searchbutton,
2796                         FALSE, FALSE, 0);
2797
2798     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_srchagainbutton,
2799                         FALSE, FALSE, 0);
2800
2801     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_zoomoutbutton,
2802                         FALSE, FALSE, 0);
2803
2804     gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_hbox,
2805                         TRUE, TRUE, 0);
2806
2807     gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_hmenubox,
2808                         FALSE, FALSE, 0);
2809
2810
2811     s_view1_hmenubox2 = gtk_hbox_new(FALSE, 5);
2812
2813     s_view1_snapbutton = gtk_button_new_with_label("Snap");
2814
2815     s_view1_nextbutton = gtk_button_new_with_label("Next");
2816
2817     s_view1_delbutton = gtk_button_new_with_label("Del");
2818
2819     s_view1_chase_event_button = gtk_button_new_with_label("ChaseEvent");
2820
2821     s_view1_chase_datum_button = gtk_button_new_with_label("ChaseDatum");
2822
2823     s_view1_chase_track_button = gtk_button_new_with_label("ChaseTrack");
2824
2825     s_view1_unchasebutton = gtk_button_new_with_label("NoChase");
2826
2827     s_view1_forward_button = gtk_button_new_with_label("->SrchChase(is<-)");
2828     s_view1_backward_button = gtk_button_new_with_label("<-SrchChase(is->)");
2829
2830     s_view1_summary_button = gtk_button_new_with_label("Summary");
2831     s_view1_nosummary_button = gtk_button_new_with_label("NoSummary");
2832
2833     s_view1_time_slew_left_button = gtk_button_new_with_label("<-TimeSlew");
2834     s_view1_time_slew_right_button = gtk_button_new_with_label("TimeSlew->");
2835
2836     gtk_signal_connect (GTK_OBJECT(s_view1_snapbutton), "clicked",
2837                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2838                         (gpointer) SNAP_BUTTON);
2839
2840     gtk_signal_connect (GTK_OBJECT(s_view1_nextbutton), "clicked",
2841                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2842                         (gpointer) NEXT_BUTTON);
2843
2844     gtk_signal_connect (GTK_OBJECT(s_view1_delbutton), "clicked",
2845                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2846                         (gpointer) DEL_BUTTON);
2847
2848     gtk_signal_connect (GTK_OBJECT(s_view1_chase_event_button), "clicked",
2849                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2850                         (gpointer) CHASE_EVENT_BUTTON);
2851
2852     gtk_signal_connect (GTK_OBJECT(s_view1_chase_datum_button), "clicked",
2853                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2854                         (gpointer) CHASE_DATUM_BUTTON);
2855
2856     gtk_signal_connect (GTK_OBJECT(s_view1_chase_track_button), "clicked",
2857                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2858                         (gpointer) CHASE_TRACK_BUTTON);
2859
2860     gtk_signal_connect (GTK_OBJECT(s_view1_unchasebutton), "clicked",
2861                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2862                         (gpointer) UNCHASE_BUTTON);
2863
2864     gtk_signal_connect (GTK_OBJECT(s_view1_forward_button), "clicked",
2865                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2866                         (gpointer) FORWARD_BUTTON);
2867
2868     gtk_signal_connect (GTK_OBJECT(s_view1_backward_button), "clicked",
2869                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2870                         (gpointer) BACKWARD_BUTTON);
2871
2872     gtk_signal_connect (GTK_OBJECT(s_view1_summary_button), "clicked",
2873                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2874                         (gpointer) SUMMARY_BUTTON);
2875
2876     gtk_signal_connect (GTK_OBJECT(s_view1_nosummary_button), "clicked",
2877                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2878                         (gpointer) NOSUMMARY_BUTTON);
2879
2880     gtk_signal_connect (GTK_OBJECT(s_view1_time_slew_left_button), "clicked",
2881                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2882                         (gpointer) SLEW_LEFT_BUTTON);
2883
2884     gtk_signal_connect (GTK_OBJECT(s_view1_time_slew_right_button), "clicked",
2885                         GTK_SIGNAL_FUNC(view1_button_click_callback),
2886                         (gpointer) SLEW_RIGHT_BUTTON);
2887
2888     gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_hmenubox2,
2889                         FALSE, FALSE, 0);
2890
2891     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_snapbutton,
2892                         FALSE, FALSE, 0);
2893
2894     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_nextbutton,
2895                         FALSE, FALSE, 0);
2896
2897     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_delbutton,
2898                         FALSE, FALSE, 0);
2899
2900     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_chase_event_button,
2901                         FALSE, FALSE, 0);
2902
2903     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_chase_datum_button,
2904                         FALSE, FALSE, 0);
2905
2906     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_chase_track_button,
2907                         FALSE, FALSE, 0);
2908
2909     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_unchasebutton,
2910                         FALSE, FALSE, 0);
2911
2912     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_forward_button,
2913                         FALSE, FALSE, 0);
2914
2915     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_backward_button,
2916                         FALSE, FALSE, 0);
2917
2918     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_summary_button,
2919                         FALSE, FALSE, 0);
2920
2921     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_nosummary_button,
2922                         FALSE, FALSE, 0);
2923
2924     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2),
2925                         s_view1_time_slew_left_button,
2926                         FALSE, FALSE, 0);
2927
2928     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2),
2929                         s_view1_time_slew_right_button,
2930                         FALSE, FALSE, 0);
2931
2932     gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2),
2933                         s_view1_anomalythresholdbutton,
2934                         FALSE, FALSE, 0);
2935
2936     s_view1_label = gtk_label_new(NULL);
2937
2938     gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_label,
2939                         FALSE, FALSE, 0);
2940
2941     gtk_box_pack_start (GTK_BOX(g_mainhbox), s_view1_vbox,
2942                         TRUE, TRUE, 0);
2943
2944     gtk_widget_show_all (s_view1_vbox);
2945     GTK_WIDGET_SET_FLAGS(da, GTK_CAN_FOCUS);
2946     gtk_widget_grab_focus(da);
2947
2948     gtk_widget_hide (s_view1_forward_button);
2949     gtk_widget_hide (summary_mode ? s_view1_summary_button
2950                                   : s_view1_nosummary_button);
2951
2952     zi_source = gdk_bitmap_create_from_data (NULL, (char *)zi_bits, zi_width,
2953                                              zi_height);
2954     zi_mask = gdk_bitmap_create_from_data (NULL, (char *)zi_bkgd, zi_width,
2955                                            zi_height);
2956
2957     zi_cursor = (GdkCursor *) gdk_cursor_new_from_pixmap (zi_source,
2958                                                           zi_mask, &fg_black,
2959                                                           &bg_white, zi_x_hot,
2960                                                           zi_y_hot);
2961     gdk_pixmap_unref (zi_source);
2962     gdk_pixmap_unref (zi_mask);
2963
2964     norm_cursor = (GdkCursor *) gdk_cursor_new (GDK_TOP_LEFT_ARROW);
2965 }
2966
2967 /****************************************************************************
2968 * line_print
2969 ****************************************************************************/
2970
2971 void line_print (int x1, int y1, int x2, int y2)
2972 {
2973     fprintf(s_printfp, "newpath\n");
2974     fprintf(s_printfp, "%d %d moveto\n", xrt(x1, s_v1->total_height - y1),
2975             yrt(x1, s_v1->total_height - y1));
2976
2977     fprintf(s_printfp, "%d %d lineto\n", xrt (x2, s_v1->total_height - y2),
2978             yrt (x2, s_v1->total_height - y2));
2979     fprintf(s_printfp, "1 setlinewidth\n");
2980     fprintf(s_printfp, "stroke\n");
2981 }
2982
2983 /****************************************************************************
2984 * tbox_print
2985 ****************************************************************************/
2986 GdkRectangle *tbox_print (char *s, int x, int y, enum view1_tbox_fn function,
2987                           GdkRectangle *rp)
2988 {
2989     if (function == TBOX_PRINT_BOXED) {
2990         rp->width -= 4;
2991     }
2992
2993     if ((function == TBOX_PRINT_BOXED) ||
2994         (function == TBOX_PRINT_EVENT)) {
2995
2996         fprintf(s_printfp, "newpath\n");
2997         fprintf(s_printfp, "0 setlinewidth\n");
2998         fprintf(s_printfp, "%d %d moveto\n",
2999                 xrt(rp->x, s_v1->total_height - rp->y),
3000                 yrt(rp->x, s_v1->total_height - rp->y));
3001
3002         fprintf(s_printfp, "%d %d lineto\n",
3003                 xrt (rp->x+rp->width, s_v1->total_height - rp->y),
3004                 yrt (rp->x+rp->width, s_v1->total_height - rp->y));
3005
3006         fprintf(s_printfp, "%d %d lineto\n",
3007                 xrt(rp->x+rp->width, s_v1->total_height - (rp->y+rp->height)),
3008                 yrt(rp->x+rp->width, s_v1->total_height - (rp->y+rp->height)));
3009
3010         fprintf(s_printfp, "%d %d lineto\n",
3011                 xrt(rp->x, s_v1->total_height - (rp->y+rp->height)),
3012                 yrt(rp->x, s_v1->total_height - (rp->y+rp->height)));
3013
3014         fprintf(s_printfp, "%d %d lineto\n",
3015                 xrt(rp->x, s_v1->total_height - rp->y),
3016                 yrt(rp->x, s_v1->total_height - rp->y));
3017
3018         fprintf(s_printfp, "stroke\n");
3019     }
3020
3021     if ((function == TBOX_PRINT_BOXED) ||
3022         (function == TBOX_PRINT_PLAIN)) {
3023
3024         fprintf(s_printfp, "newpath\n");
3025         fprintf(s_printfp, "%d %d moveto\n",
3026                 xrt(x, s_v1->total_height - (y-2)),
3027                 yrt(x, s_v1->total_height - (y-2)));
3028         fprintf(s_printfp, "gsave\n");
3029         fprintf(s_printfp, "90 rotate\n");
3030         fprintf(s_printfp, "(%s) show\n", s);
3031         fprintf(s_printfp, "grestore\n");
3032     }
3033
3034     return(rp);
3035 }
3036
3037 /****************************************************************************
3038 * tbox - draws an optionally boxed string whose lower lefthand
3039 * corner is at (x, y).  As usual, Y is backwards.
3040 ****************************************************************************/
3041
3042 GdkRectangle *tbox (char *s, int x, int y, enum view1_tbox_fn function)
3043 {
3044     static GdkRectangle update_rect;
3045     gint lbearing, rbearing, width, ascent, descent;
3046
3047     gdk_string_extents (g_font, s,
3048                         &lbearing, &rbearing,
3049                         &width, &ascent, &descent);
3050
3051     /*
3052      * If we have enough room to display full size events, then just
3053      * use the BOXED function instead of the EVENT function.
3054      */
3055     if (s_v1->strip_height > 9) {
3056         switch (function) {
3057         case TBOX_DRAW_EVENT:    function = TBOX_DRAW_BOXED;    break;
3058         case TBOX_GETRECT_EVENT: function = TBOX_GETRECT_BOXED; break;
3059         case TBOX_PRINT_EVENT:   function = TBOX_PRINT_BOXED;   break;
3060         default:
3061             break;
3062             /* Nothing */
3063         }
3064     }
3065
3066     switch (function) {
3067     case TBOX_DRAW_BOXED:
3068         gdk_draw_rectangle (pm, da->style->white_gc, TRUE,
3069                             x, y - (ascent+descent+3), width + 2,
3070                             ascent + descent + 3);
3071
3072         gdk_draw_rectangle (pm, da->style->black_gc, FALSE,
3073                             x, y - (ascent+descent+3), width + 2,
3074                             ascent + descent + 3);
3075
3076         gdk_draw_string (pm, g_font, da->style->black_gc,
3077                          x + 1, y - 1, (const gchar *)s);
3078         /* NOTE FALLTHROUGH */
3079     case TBOX_GETRECT_BOXED:
3080         update_rect.x = x;
3081         update_rect.y = y -(ascent+descent+3);
3082         update_rect.width = width + 3;
3083         update_rect.height = ascent + descent + 4;
3084         if (function == TBOX_DRAW_BOXED)
3085             gtk_widget_draw (da, &update_rect);
3086         break;
3087
3088     case TBOX_DRAW_EVENT:
3089         /* We have a small event to draw...no text */
3090         gdk_draw_rectangle (pm, da->style->black_gc, FALSE,
3091                             x, y - 1, 3, 3);
3092         /* NOTE FALLTHROUGH */
3093     case TBOX_GETRECT_EVENT:
3094         update_rect.x = x;
3095         update_rect.y = y - 1;
3096         update_rect.width = 4;
3097         update_rect.height = 4;
3098         if (function == TBOX_DRAW_EVENT)
3099             gtk_widget_draw (da, &update_rect);
3100         break;
3101
3102
3103     case TBOX_DRAW_PLAIN:
3104
3105         gdk_draw_string (pm, g_font, da->style->black_gc,
3106                          x + 1, y - 1, (const gchar *)s);
3107         /* NOTE FALLTHROUGH */
3108     case TBOX_GETRECT_PLAIN:
3109         update_rect.x = x;
3110         update_rect.y = y -(ascent+descent+1);
3111         update_rect.width = width;
3112         update_rect.height = ascent + descent;
3113         if (function == TBOX_DRAW_PLAIN)
3114             gtk_widget_draw (da, &update_rect);
3115         break;
3116
3117     case TBOX_PRINT_BOXED:
3118         update_rect.x = x;
3119         update_rect.y = y -(ascent+descent+3);
3120         update_rect.width = width + 3;
3121         update_rect.height = ascent + descent + 4;
3122         /* note fallthrough */
3123     case TBOX_PRINT_PLAIN:
3124         return(tbox_print(s, x, y, function, &update_rect));
3125
3126     case TBOX_PRINT_EVENT:
3127         /* We have a small event box to print...no text */
3128         update_rect.x = x;
3129         update_rect.y = y - 1;
3130         update_rect.width = 4;
3131         update_rect.height = 4;
3132         return(tbox_print(s, x, y, function, &update_rect));
3133     }
3134     return(&update_rect);
3135 }
3136
3137 /****************************************************************************
3138 * line
3139 *
3140 * For lines there is a primitive batching facility, that doesn't update
3141 * the drawing area until the batch is complete. This is handy for drawing
3142 * the pid axis and for summary mode.
3143 *
3144 * line_batch_mode contains the state for this:
3145 *
3146 *   BATCH_OFF:      no batching, update for every line
3147 *   BATCH_NEW:      just entered a batch, so initialize the area to update from
3148 *                   scratch
3149 *   BATCH_EXISTING: have drawn at least one line in batch mode, so the update
3150 *                   area should only be expanded from now on to include the
3151 *                   union of the "rectangular hull" of all lines
3152 ****************************************************************************/
3153
3154 static enum { BATCH_OFF, BATCH_NEW, BATCH_EXISTING } line_batch_mode;
3155 static int line_batch_count;
3156 static int line_minx, line_miny, line_maxx, line_maxy;
3157
3158 void line_batch_start (void)
3159 {
3160     line_batch_mode = BATCH_NEW;
3161     line_batch_count = 0;
3162 }
3163
3164 void line_batch_end (void)
3165 {
3166     GdkRectangle update_rect;
3167     if (line_batch_count > 0) {
3168         update_rect.x = line_minx;
3169         update_rect.y = line_miny;
3170         update_rect.width = (line_maxx - line_minx) + 1;
3171         update_rect.height = (line_maxy - line_miny) + 1;
3172         gtk_widget_draw (da, &update_rect);
3173     }
3174     line_batch_mode = BATCH_OFF;
3175 }
3176
3177 void line (int x1, int y1, int x2, int y2, enum view1_line_fn function)
3178 {
3179     GdkRectangle update_rect;
3180     GdkGC *gc = NULL;
3181
3182     switch(function) {
3183     case LINE_DRAW_BLACK:
3184         gc = da->style->black_gc;
3185         break;
3186
3187     case LINE_DRAW_WHITE:
3188         gc = da->style->white_gc;
3189         break;
3190
3191     case LINE_PRINT:
3192         line_print (x1, y1, x2, y2);
3193         return;
3194     }
3195
3196     gdk_draw_line (pm, gc, x1, y1, x2, y2);
3197
3198     switch (line_batch_mode) {
3199         case BATCH_OFF:
3200             update_rect.x = x1;
3201             update_rect.y = y1;
3202             update_rect.width = (x2-x1) + 1;
3203             update_rect.height = (y2-y1) + 1;
3204             gtk_widget_draw (da, &update_rect);
3205             break;
3206
3207         case BATCH_NEW:
3208             line_minx = x1;
3209             line_maxx = x2;
3210             line_miny = y1;
3211             line_maxy = y2;
3212             line_batch_mode = BATCH_EXISTING;
3213             line_batch_count = 1;
3214             break;
3215
3216         case BATCH_EXISTING:
3217             if (line_minx > x1)
3218                 line_minx = x1;
3219             if (line_miny > y1)
3220                 line_miny = y1;
3221             if (line_maxx < x2)
3222                 line_maxx = x2;
3223             if (line_maxy < y2)
3224                 line_maxy = y2;
3225             line_batch_count++;
3226             break;
3227     }
3228 }
3229
3230
3231 /****************************************************************************
3232 * display_pid_axis
3233 ****************************************************************************/
3234
3235 static void display_pid_axis(v1_geometry_t *vp)
3236 {
3237     int y, i, label_tick;
3238     int last_printed_y = -vp->strip_height;
3239     pid_sort_t *pp;
3240     int pid_index;
3241     char *label_fmt;
3242     char tmpbuf [128];
3243
3244     /* No pids yet? Outta here */
3245     if (g_pids == NULL)
3246         return;
3247
3248     line_batch_start();
3249
3250     for (i = 0; i < vp->npids; i++) {
3251         pid_index = vp->first_pid_index + i;
3252         if (pid_index >= g_npids)
3253             break;
3254
3255         pp = (g_pids + pid_index);
3256
3257         set_color(pid_index);
3258
3259         label_fmt = get_track_label(pp->pid_value);
3260         snprintf(tmpbuf, sizeof(tmpbuf)-1, label_fmt, pp->pid_value);
3261
3262         y = i*vp->strip_height + vp->pid_ax_offset;
3263
3264         /*
3265          * Have we incremented enough space to have another label not
3266          * overlap the previous label?
3267          */
3268         if (y - last_printed_y > 9) {
3269             /* Draw label */
3270             tbox(tmpbuf, 0, y +4, TBOX_DRAW_PLAIN+s_print_offset);
3271
3272             last_printed_y = y;
3273
3274             /*
3275              * And let the line stick out a bit more to indicate this label
3276              * relates to the following line.
3277              */
3278             label_tick = 4;
3279         }
3280         else {
3281             label_tick = 0;
3282         }
3283
3284         /* Draw axis line, but only if the lines aren't too close together */
3285         if (vp->strip_height > 4) {
3286             line(vp->pid_ax_width - label_tick, y+4*s_print_offset,
3287                  vp->total_width, y+4*s_print_offset,
3288                  LINE_DRAW_BLACK+s_print_offset);
3289         }
3290     }
3291
3292     set_color(COLOR_DEFAULT);
3293     line_batch_end();
3294 }
3295
3296 /****************************************************************************
3297 * view1_read_events_callback
3298 * New event data just showed up, reset a few things.
3299 ****************************************************************************/
3300
3301 void view1_read_events_callback(void)
3302 {
3303     int max_vis_index;
3304
3305     s_v1->first_pid_index = 0;
3306
3307     max_vis_index = 300;
3308     if (max_vis_index > g_nevents)
3309         max_vis_index = g_nevents-1;
3310
3311     s_v1->minvistime = 0LL;
3312     s_v1->maxvistime = (g_events[g_nevents - 1].time * 9)/ 8;
3313     /* Single event? Make the initial display 1s wide */
3314     if (g_nevents == 1)
3315         s_v1->maxvistime = 1000000;
3316     s_srchindex = 0;
3317     s_srchcode = 0;
3318     s_last_selected_event = 0;
3319
3320     init_track_colors();
3321
3322     recompute_hscrollbar();
3323     recompute_vscrollbar();
3324 }
3325
3326 /****************************************************************************
3327 * display_event_data
3328 ****************************************************************************/
3329
3330 static void display_event_data(v1_geometry_t *vp)
3331 {
3332     int start_index;
3333     int pid_index;
3334     int x, y;
3335     event_t *ep;
3336     event_def_t *edp;
3337     double time_per_pixel;
3338     char tmpbuf[1024];
3339     GdkRectangle *print_rect;
3340     int *last_x_used;
3341
3342     /* Happens if one loads the event def header first, for example. */
3343     if (g_nevents == 0)
3344         return;
3345
3346     time_per_pixel = dtime_per_pixel(vp);
3347
3348     start_index = find_event_index (vp->minvistime);
3349
3350     /* Scrolled too far right? */
3351     if (start_index >= g_nevents)
3352         return;
3353
3354     ep = (g_events + start_index);
3355
3356     if (s_print_offset || summary_mode) {
3357         last_x_used = (int *)g_malloc0(vp->npids * sizeof(int));
3358     } else {
3359         last_x_used = NULL;
3360     }
3361
3362     line_batch_start();
3363
3364     while (ep < (g_events + g_nevents) &&
3365            (ep->time < vp->maxvistime)) {
3366         pid_index = ep->pid->pid_index;
3367         set_color(pid_index);
3368
3369         /* First filter: pid out of range */
3370         if ((pid_index < vp->first_pid_index) ||
3371             (pid_index >= vp->first_pid_index + vp->npids)) {
3372             ep++;
3373             continue;
3374         }
3375
3376         /* Second filter: event hidden */
3377         edp = find_event_definition (ep->code);
3378         if (!edp->selected) {
3379             ep++;
3380             continue;
3381         }
3382
3383         /* Display it... */
3384
3385         pid_index -= vp->first_pid_index;
3386
3387         y = pid_index*vp->strip_height + vp->event_offset;
3388
3389         x = vp->pid_ax_width +
3390             (int)(((double)(ep->time - vp->minvistime)) / time_per_pixel);
3391
3392         if (last_x_used != NULL && x < last_x_used[pid_index]) {
3393             ep++;
3394             continue;
3395         }
3396
3397         if (ep->flags & (EVENT_FLAG_SELECT | EVENT_FLAG_SEARCHRSLT)) {
3398             if (ep->flags & EVENT_FLAG_SELECT) {
3399                 format_popbox_string(tmpbuf, sizeof(tmpbuf), ep, edp);
3400             } else {
3401                 snprintf(tmpbuf, sizeof(tmpbuf), "SEARCH RESULT");
3402             }
3403             print_rect = tbox(tmpbuf, x, y - vp->pop_offset,
3404                               TBOX_DRAW_BOXED+s_print_offset);
3405             line(x, y-vp->pop_offset, x, y, LINE_DRAW_BLACK+s_print_offset);
3406             if (last_x_used != NULL)
3407                 last_x_used[pid_index] = x + print_rect->width;
3408         }
3409         if (summary_mode) {
3410             int delta = vp->strip_height / 3;
3411             if (delta < 1)
3412                 delta = 1;
3413             y = pid_index*vp->strip_height + vp->pid_ax_offset;
3414             line(x, y - delta, x, y + delta, LINE_DRAW_BLACK);
3415             last_x_used[pid_index] = x + 1;
3416         } else {
3417             snprintf(tmpbuf, sizeof(tmpbuf), "%ld", ep->code);
3418             print_rect = tbox(tmpbuf, x, y, TBOX_DRAW_EVENT+s_print_offset);
3419             if (last_x_used != NULL)
3420                 last_x_used[pid_index] = x + print_rect->width;
3421         }
3422
3423         ep++;
3424     }
3425     if (last_x_used)
3426         g_free(last_x_used);
3427     line_batch_end();
3428     set_color(COLOR_DEFAULT);
3429 }
3430
3431 /****************************************************************************
3432 * display_clear
3433 ****************************************************************************/
3434
3435 static void display_clear(void)
3436 {
3437     GdkRectangle update_rect;
3438
3439     gdk_draw_rectangle (pm, da->style->white_gc, TRUE,
3440                         0, 0, da->allocation.width,
3441                         da->allocation.height);
3442
3443     update_rect.x = 0;
3444     update_rect.y = 0;
3445     update_rect.width = da->allocation.width;
3446     update_rect.height = da->allocation.height;
3447
3448     gtk_widget_draw (da, &update_rect);
3449 }
3450
3451 /****************************************************************************
3452 * display_time_axis
3453 ****************************************************************************/
3454
3455 static void display_time_axis(v1_geometry_t *vp)
3456 {
3457     int x, y, i;
3458     int xoffset, nticks;
3459     char tmpbuf [128];
3460     double unit_divisor;
3461     double time;
3462     char *units;
3463     double time_per_pixel;
3464
3465     y = vp->npids * vp->strip_height + vp->pid_ax_offset;
3466
3467     x = vp->pid_ax_width;
3468
3469     nticks = (vp->total_width - vp->pid_ax_width) / vp->time_ax_spacing;
3470
3471     time_per_pixel = dtime_per_pixel(vp);
3472
3473     units = "ns";
3474     unit_divisor = 1.00;
3475
3476     if ((vp->maxvistime / unit_divisor) > 1000) {
3477         units = "us";
3478         unit_divisor = 1000.00;
3479     }
3480
3481     if ((vp->maxvistime / unit_divisor) > 1000) {
3482         units = "ms";
3483         unit_divisor = 1000.00*1000.00;
3484     }
3485     if ((vp->maxvistime / unit_divisor) > 1000) {
3486         units = "s";
3487         unit_divisor = 1000.00*1000.00*1000.00;
3488     }
3489
3490     /* Draw line */
3491     line(x, y, vp->total_width, y, LINE_DRAW_BLACK+s_print_offset);
3492
3493     xoffset = 0;
3494
3495     for (i = 0; i < nticks; i++) {
3496         /* Tick mark */
3497         line(x+xoffset, y-3, x+xoffset, y+3, LINE_DRAW_BLACK+s_print_offset);
3498
3499         time = (double)(x + xoffset - vp->pid_ax_width);
3500         time *= time_per_pixel;
3501         time += (double)(vp->minvistime);
3502         time /= unit_divisor;
3503
3504         snprintf (tmpbuf, sizeof(tmpbuf), "%.2f%s", time, units);
3505
3506         tbox(tmpbuf, x+xoffset, y+15, TBOX_DRAW_PLAIN+s_print_offset);
3507
3508         xoffset += vp->time_ax_spacing;
3509     }
3510 }
3511
3512 /****************************************************************************
3513 * clear_scoreboard
3514 * Forget about any temporary displays, they're gone now...
3515 ****************************************************************************/
3516
3517 static void clear_scoreboard(void)
3518 {
3519     s_result_up = FALSE;
3520 }
3521
3522 /****************************************************************************
3523 * view1_display
3524 ****************************************************************************/
3525
3526 void view1_display(void)
3527 {
3528     display_clear();
3529     display_pid_axis(s_v1);
3530     display_event_data(s_v1);
3531     display_time_axis(s_v1);
3532     clear_scoreboard();
3533 }
3534
3535 static gint idle_tag;
3536
3537 /****************************************************************************
3538 * view1_display_eventually
3539 ****************************************************************************/
3540
3541 static void view1_display_eventually(void)
3542 {
3543     gtk_idle_remove(idle_tag);
3544     idle_tag = 0;
3545     view1_display();
3546 }
3547
3548
3549 /****************************************************************************
3550 * view1_display_when_idle
3551 ****************************************************************************/
3552
3553 void view1_display_when_idle(void)
3554 {
3555     if (idle_tag == 0) {
3556         idle_tag = gtk_idle_add((GtkFunction) view1_display_eventually, 0);
3557     }
3558 }
3559
3560 /****************************************************************************
3561 * view1_about
3562 ****************************************************************************/
3563
3564 void view1_about (char *tmpbuf)
3565 {
3566     int nsnaps;
3567     snapshot_t *snaps;
3568
3569     snprintf(tmpbuf+strlen(tmpbuf), 128, "Minvistime %lld\nMaxvistime %lld\n",
3570             s_v1->minvistime, s_v1->maxvistime);
3571     snprintf(tmpbuf+strlen(tmpbuf), 128, "Strip Height %d\n",
3572             s_v1->strip_height);
3573
3574     for (nsnaps = 0, snaps = s_snapshots; snaps; snaps = snaps->next) {
3575         nsnaps++;
3576     }
3577     snprintf(tmpbuf+strlen(tmpbuf), 128, "%d snapshots in the ring\n", nsnaps);
3578 }