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