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