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