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