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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 #include <gdk/gdkkeysyms.h>
25 #include <vppinfra/format.h>
26 #include <vppinfra/elog.h>
29 * The main event display view.
31 * Important variables:
33 * "da" -- the drawing area, aka the screen representation of the
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.
41 * "s_v1" -- pointer to the current v1_geometry_t object.
47 * s_view1_topbutton("Top")
48 * s_view1_vscroll (vertical scrollbar)
49 * s_view1_bottombutton("Bottom")
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")
65 GdkFont *g_font; /* a fixed-width font to use */
66 GdkColor fg_black = {0, 0, 0, 0};
67 GdkColor bg_white = {0, 65535, 65535, 65535};
68 static boolean summary_mode = TRUE; /* start out in summary mode */
69 static boolean color_mode = FALSE; /* start out in color mode */
76 * user_data values passed to view1_button_click_callback,
77 * which is used by the various action buttons noted above
79 enum view1_button_click {
110 SRCH_CHASE_FORWARD = 0,
111 SRCH_CHASE_BACKWARD = 1,
114 static GtkWidget *s_view1_hbox; /* see box heirarchy chart */
115 static GtkWidget *s_view1_vbox; /* see box heirarchy chart */
116 static GtkWidget *da; /* main drawing area */
117 static GdkPixmap *pm; /* and its backing pixmap */
118 static GdkCursor *norm_cursor; /* the "normal" cursor */
121 * view geometry parameters
124 * Y increases down the page.
125 * Strip origin is at the top
127 * Don't put your fingers in your mouth.
129 * Most of these values are in pixels
132 typedef struct v1_geometry {
133 int pid_ax_width; /* Width of the PID axis */
134 int time_ax_height; /* Height of the time axis */
135 int time_ax_spacing; /* TimeAxis: Space between tick-marks */
136 int strip_height; /* Height of a regular PID trace */
137 int pop_offset; /* Vertical offset of the detail box */
138 int pid_ax_offset; /* Vertical offset of the PID axis */
139 int event_offset; /* Vertical offset of the event boxes */
140 int total_height; /* total height of da, see configure_event */
141 int total_width; /* ditto, for width */
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 */
151 /* The active geometry object */
152 static v1_geometry_t s_v1record;
153 static v1_geometry_t *s_v1 = &s_v1record;
155 /* The color array */
156 static GdkColor *s_color;
159 typedef struct snapshot {
160 struct snapshot *next;
161 /* Screen geometry */
162 v1_geometry_t geometry;
163 boolean show_event[NEVENTS];
166 * Note: not worth recomputing the vertical scrollbar, just save
169 gfloat vscroll_value;
170 boolean summary_mode;
174 static snapshot_t *s_snapshots;
175 static snapshot_t *s_cursnap;
176 static event_t *s_last_selected_event;
179 * various widgets, see the box heirarchy chart above
180 * The toolkit keeps track of these things, we could lose many of
183 static GtkWidget *s_view1_vmenubox;
184 static GtkWidget *s_view1_topbutton;
185 static GtkWidget *s_view1_bottombutton;
186 static GtkWidget *s_view1_more_traces_button;
187 static GtkWidget *s_view1_less_traces_button;
189 static GtkWidget *s_view1_hmenubox;
190 static GtkWidget *s_view1_hmenubox2;
191 static GtkWidget *s_view1_startbutton;
192 static GtkWidget *s_view1_zoominbutton;
193 static GtkWidget *s_view1_searchbutton;
194 static GtkWidget *s_view1_srchagainbutton;
195 static GtkWidget *s_view1_zoomoutbutton;
196 static GtkWidget *s_view1_endbutton;
198 static GtkWidget *s_view1_snapbutton;
199 static GtkWidget *s_view1_nextbutton;
200 static GtkWidget *s_view1_delbutton;
202 static GtkWidget *s_view1_chase_event_button;
203 static GtkWidget *s_view1_chase_datum_button;
204 static GtkWidget *s_view1_chase_track_button;
205 static GtkWidget *s_view1_unchasebutton;
207 static GtkWidget *s_view1_forward_button;
208 static GtkWidget *s_view1_backward_button;
210 static GtkWidget *s_view1_summary_button;
211 static GtkWidget *s_view1_nosummary_button;
213 static GtkWidget *s_view1_hscroll;
214 static GtkObject *s_view1_hsadj;
216 static GtkWidget *s_view1_vscroll;
217 static GtkObject *s_view1_vsadj;
219 static GtkWidget *s_view1_label;
224 static ulong s_srchcode; /* search event code */
225 static int s_srchindex; /* last hit was at this event index */
226 static boolean s_result_up; /* The SEARCH RESULT dongle is displayed */
227 static boolean s_srchfail_up; /* The status line "Search Failed" is up */
228 static int srch_chase_dir; /* search/chase dir, 0=>forward */
234 static int s_print_offset; /* Magic offset added to line, tbox fn codes */
235 static FILE *s_printfp;
238 * Forward reference prototypes
240 static void display_pid_axis(v1_geometry_t *vp);
241 static void display_event_data(v1_geometry_t *vp);
242 static void display_time_axis(v1_geometry_t *vp);
243 static void view1_button_click_callback(GtkButton *item, gpointer data);
249 gint c_view1_draw_width;
250 gint c_view1_draw_height;
253 * Zoom-In / Time Ruler cursor
260 static unsigned char zi_bits[] = {
261 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
262 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
263 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
264 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x88, 0x00,
265 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xc0, 0x00,
266 0x00, 0xfc, 0xff, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xa0, 0x00,
267 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x84, 0x00,
268 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
269 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
270 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
271 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
273 static unsigned char zi_bkgd[] = {
274 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
275 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
276 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
277 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x88, 0x00,
278 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xc0, 0x00,
279 0x00, 0xfc, 0xff, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xa0, 0x00,
280 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x84, 0x00,
281 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
282 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
283 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
284 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
286 static GdkCursor *zi_cursor;
287 static GdkPixmap *zi_source, *zi_mask;
290 * Frequently-used small computations, best
291 * done correctly once and instantiated.
294 /****************************************************************************
296 ****************************************************************************/
298 static inline double dtime_per_pixel(v1_geometry_t *vp)
300 return ((double)(vp->maxvistime - vp->minvistime)) /
301 ((double)(vp->total_width - vp->pid_ax_width));
304 /****************************************************************************
306 * Changes the status line. Pass "" to clear the status line.
307 ****************************************************************************/
309 void message_line (char *s)
311 gtk_label_set_text (GTK_LABEL(s_view1_label), s);
314 /****************************************************************************
316 * Changes the window title to include the specified filename.
317 ****************************************************************************/
319 void set_window_title (const char *filename)
322 snprintf(title, sizeof(title), "g2 (%s)", filename);
323 gtk_window_set_title(GTK_WINDOW(g_mainwindow), title);
326 /****************************************************************************
327 * recompute_hscrollbar
328 * Adjust the horizontal scrollbar's adjustment object.
330 * GtkAdjustments are really cool, but have to be set up exactly
331 * right or the various client objects screw up completely.
333 * Note: this function is *not* called when the user clicks the scrollbar.
334 ****************************************************************************/
336 static void recompute_hscrollbar (void)
338 ulonglong current_width;
339 ulonglong event_incdec;
346 ep = (g_events + (g_nevents-1));
347 current_width = s_v1->maxvistime - s_v1->minvistime;
348 event_incdec = (current_width) / 6;
350 adj = GTK_ADJUSTMENT(s_view1_hsadj);
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
363 adj->lower = (gfloat)0.00;
364 adj->value = (gfloat)s_v1->minvistime;
366 /* Minor click: move about 1/6 of a page */
367 adj->step_increment = (gfloat)event_incdec;
369 /* Major click: move about 1/3 of a page. */
370 adj->page_increment = (gfloat)(2*event_incdec);
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);
377 * Tell all clients (e.g. the visible scrollbar) to
378 * make themselves look right
380 gtk_adjustment_changed(adj);
381 gtk_adjustment_value_changed(adj);
384 /****************************************************************************
385 * recompute_vscrollbar
386 * Ditto, for the vertical scrollbar
387 ****************************************************************************/
389 static void recompute_vscrollbar (void)
393 adj = GTK_ADJUSTMENT(s_view1_vsadj);
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);
405 /****************************************************************************
406 * format_popbox_string
407 ****************************************************************************/
409 elog_main_t elog_main;
411 void format_popbox_string (char *tmpbuf, int len, event_t *ep, event_def_t *edp)
416 sprintf(tmpbuf,"%d:", ep->code);
418 if (ep->flags & EVENT_FLAG_CLIB) {
422 eep = get_clib_event (ep->datum);
424 s = format (0, "%U", format_elog_event, &elog_main, eep);
425 memcpy (tmpbuf, s, vec_len(s));
426 tmpbuf[vec_len(s)] = 0;
431 snprintf(tmpbuf, len, "%s", edp->name);
433 /* Make sure there's a real format string. If so, add it */
436 snprintf(tmpbuf+strlen(tmpbuf), len - strlen(tmpbuf), ": ");
437 /* %s only supported for cpel files */
439 snprintf(tmpbuf+strlen(tmpbuf), len - strlen(tmpbuf),
440 edp->format, strtab_ref(ep->datum));
442 snprintf(tmpbuf+strlen(tmpbuf), len - strlen(tmpbuf),
443 edp->format, ep->datum);
451 /****************************************************************************
453 ****************************************************************************/
455 static void add_snapshot(void)
458 snapshot_t *new = g_malloc(sizeof(snapshot_t));
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;
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;
471 new->next = s_snapshots;
480 /****************************************************************************
482 ****************************************************************************/
484 static void next_snapshot(void)
492 infobox("No snapshots", "\nNo snapshots in the ring...\n");
496 next = s_cursnap->next;
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];
506 memcpy(g_pids, next->pidvec, sizeof(pid_sort_t)*g_npids);
507 color_mode = next->color_mode;
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.)
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));
519 /* Fix the pid structure index mappings */
522 for (i = 0; i < g_npids; i++) {
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();
535 /****************************************************************************
537 ****************************************************************************/
539 static void del_snapshot(void)
545 infobox("No snapshots", "\nNo snapshots to delete...\n");
552 while (this && this != s_cursnap) {
557 if (this != s_cursnap) {
558 infobox("BUG", "\nSnapshot AWOL!\n");
562 s_cursnap = this->next;
564 /* middle of the list? */
566 prev->next = this->next;
567 g_free(this->pidvec);
569 } else { /* start of the list */
570 s_snapshots = this->next;
571 g_free(this->pidvec);
575 /* Note: both will be NULL after last delete */
576 if (s_cursnap == NULL)
577 s_cursnap = s_snapshots;
580 /****************************************************************************
583 * VERY primitive right now - not endian or version independent, and only
584 * writes to "snapshots.g2" in the current directory
585 ****************************************************************************/
586 static void write_snapshot(void)
593 if (s_snapshots == NULL) {
594 error = "No snapshots defined";
599 file = fopen("snapshots.g2", "w");
601 error = "Unable to open snapshots.g2";
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.
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 ||
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";
629 error = "Unable to close file";
634 infobox(error, strerror(errno));
637 snprintf(buf, sizeof(buf), "Wrote %d snapshots to snapshots.g2",
643 /****************************************************************************
646 * VERY primitive right now - not endian or version independent, and only reads
647 * from "snapshots.g2" in the current directory
648 ****************************************************************************/
649 static void read_snapshot(void)
652 snapshot_t *snap, *next_snap;
653 snapshot_t *new_snaps = NULL;
655 int len, i, records = 0;
658 file = fopen("snapshots.g2", "r");
660 error = "Unable to open snapshots.g2";
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.
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 */
672 len = fread(&snap->geometry, sizeof(snap->geometry), 1, file);
678 /* insert into list straight away */
679 snap->next = new_snaps;
683 error = "Problem reading first item from file";
686 if (fread(&snap->show_event, sizeof(snap->show_event), 1, file) != 1) {
687 error = "Problem reading second item from file";
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";
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";
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.
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) {
719 snap->pidvec[i].pid = pp;
721 error = "Snapshot file referenced unknown pids";
731 error = "Unable to close file";
737 * Problem - clear up any detritus
739 infobox(error, strerror(errno));
740 for (snap = new_snaps; snap != NULL; snap = next_snap) {
741 next_snap = snap->next;
743 g_free(snap->pidvec);
747 * Success! trash the old snapshots and replace with the new
749 for (snap = s_snapshots; snap != NULL; snap = next_snap) {
750 next_snap = snap->next;
751 g_free(snap->pidvec);
755 s_cursnap = s_snapshots = new_snaps;
759 infobox(error, strerror(errno));
762 snprintf(buf, sizeof(buf),
763 "Read %d snapshots from snapshots.g2", records);
768 /****************************************************************************
771 * Set the color for the specified pid_index, or COLOR_DEFAULT to return it
772 * to the usual black.
773 ****************************************************************************/
774 #define COLOR_DEFAULT (-1)
775 static void set_color(int pid_index)
777 if (pid_index == COLOR_DEFAULT || !color_mode) {
778 gdk_gc_set_foreground(da->style->black_gc, &fg_black);
780 gdk_gc_set_foreground(da->style->black_gc,
781 &s_color[g_pids[pid_index].color_index]);
785 /****************************************************************************
786 * toggle_event_select
787 ****************************************************************************/
789 static void toggle_event_select(GdkEventButton *event, v1_geometry_t *vp)
791 int pid_index, start_index;
794 GdkRectangle hit_rect;
799 double time_per_pixel;
804 time_per_pixel = dtime_per_pixel(vp);
806 start_index = find_event_index (vp->minvistime);
809 if (start_index >= g_nevents)
813 * To see if the mouse hit a visible event, use a variant
814 * of the event display loop.
817 hit_rect.x = (int)event->x;
818 hit_rect.y = (int)event->y;
822 ep = (g_events + start_index);
824 while ((ep->time < vp->maxvistime) &&
825 (ep < (g_events + g_nevents))) {
826 pid_index = ep->pid->pid_index;
828 /* First filter: pid out of range */
829 if ((pid_index < vp->first_pid_index) ||
830 (pid_index >= vp->first_pid_index + vp->npids)) {
835 /* Second filter: event hidden */
836 edp = find_event_definition (ep->code);
837 if (!edp->selected) {
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
848 * $$$$ maybe keep looping until off the edge,
849 * maintain a "best hit", then declare that one the winner?
852 pid_index -= vp->first_pid_index;
854 y = pid_index*vp->strip_height + vp->event_offset;
856 x = vp->pid_ax_width +
857 (int)(((double)(ep->time - vp->minvistime)) / time_per_pixel);
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();
871 sprintf(tmpbuf, "%ld", ep->code);
873 /* Figure out the dimensions of the regular box */
874 rp = tbox(tmpbuf, x, y, TBOX_GETRECT_EVENT);
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();
883 set_color(ep->pid->pid_index);
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;
899 /****************************************************************************
901 ****************************************************************************/
903 typedef enum { MOVE_TOP, MOVE_BOTTOM } move_type;
905 static void move_current_track(GdkEventButton *event,
912 pid_sort_t *new_pidvec;
914 pid_sort_t *pold, *pnew;
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;
931 infobox("NOTE", "\nNo PID/Track In Range\nPlease Try Again");
935 pid_index = i + vp->first_pid_index;
937 new_pidvec = g_malloc(sizeof(pid_sort_t)*g_npids);
941 if (type == MOVE_TOP) {
943 *pnew++ = g_pids[pid_index];
944 for (i = 0; i < pid_index; i++)
948 for (; i < g_npids; i++)
952 for (i = 0; i < pid_index; i++)
956 for (; i < g_npids; i++)
958 *pnew = g_pids[pid_index];
965 * Revert the pid_index mapping to an identity map,
969 for (i = 0; i < g_npids; i++) {
974 view1_display_when_idle();
977 /****************************************************************************
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 ****************************************************************************/
984 void zoom_event(GdkEventButton *e1, GdkEventButton *e2, v1_geometry_t *vp)
987 double time_per_pixel;
988 double width_in_pixels;
989 double center_on_time, width_in_time;
990 double center_on_pixel;
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
998 if (e1->x < vp->pid_ax_width)
999 e1->x = vp->pid_ax_width;
1001 if (e2->x < vp->pid_ax_width)
1002 e2->x = vp->pid_ax_width;
1005 goto loser_zoom_repaint;
1007 xrange = (double) (e2->x - e1->x);
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;
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;
1022 * Transform back to 64-bit integer microseconds, reset the
1023 * scrollbar, schedule a repaint.
1025 vp->minvistime = (ulonglong)(center_on_time - width_in_time);
1026 vp->maxvistime = (ulonglong)(center_on_time + width_in_time);
1029 recompute_hscrollbar();
1031 view1_display_when_idle();
1034 /****************************************************************************
1037 * Scroll up or down by the specified delta
1039 ****************************************************************************/
1040 static void scroll_y(int delta)
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;
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();
1056 /****************************************************************************
1057 * view1_handle_key_press_event
1058 * Relevant definitions in: /usr/include/gtk-1.2/gdk/gdktypes.h
1060 * This routine implements hotkeys for the Quake generation:
1071 * E - toggle summary mode
1072 * C - toggle color mode
1076 * P - persist snapshots to file
1077 * L - load snapshots from file
1081 ****************************************************************************/
1083 view1_handle_key_press_event (GtkWidget *widget, GdkEventKey *event)
1087 switch (event->keyval) {
1088 case GDK_w: // zoom in
1089 view1_button_click_callback(NULL, (gpointer)ZOOMIN_BUTTON);
1092 case GDK_s: // zoom out
1093 view1_button_click_callback(NULL, (gpointer)ZOOMOUT_BUTTON);
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;
1101 s_v1->minvistime -= delta;
1102 s_v1->maxvistime -= delta;
1103 recompute_hscrollbar();
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) {
1110 * @@@ this doesn't seem to quite reach the far right hand
1111 * side correctly - not sure why.
1113 delta = g_events[g_nevents - 1].time - s_v1->maxvistime;
1115 s_v1->minvistime += delta;
1116 s_v1->maxvistime += delta;
1117 recompute_hscrollbar();
1120 case GDK_r: // pan up
1124 case GDK_f: // pan down
1128 case GDK_t: // fewer tracks
1129 view1_button_click_callback(NULL, (gpointer)LESS_TRACES_BUTTON);
1132 case GDK_g: // more tracks
1133 view1_button_click_callback(NULL, (gpointer)MORE_TRACES_BUTTON);
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));
1142 case GDK_c: // toggle color mode
1144 view1_display_when_idle();
1147 case GDK_p: // persist snapshots
1151 case GDK_l: // load snapshots
1155 case GDK_x: // take snapshot
1156 view1_button_click_callback(NULL, (gpointer)SNAP_BUTTON);
1159 case GDK_z: // next snapshot
1160 view1_button_click_callback(NULL, (gpointer)NEXT_BUTTON);
1163 case GDK_q: // ctrl-q is exit
1164 if (event->state & GDK_CONTROL_MASK) {
1172 /****************************************************************************
1173 * button_press_event
1174 * Relevant definitions in: /usr/include/gtk-1.2/gdk/gdktypes.h
1176 * This routine implements three functions: zoom-to-area, time ruler, and
1177 * show/hide event detail popup.
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
1184 * The right mouse button (button 3) implements the time ruler.
1185 ****************************************************************************/
1188 button_press_event (GtkWidget *widget, GdkEventButton *event)
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;
1198 double time_per_pixel;
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;
1210 if (event->button == 3) {
1211 press3_valid = TRUE;
1212 press3_event = *event;
1217 case GDK_BUTTON_RELEASE:
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();
1226 /* Event select / zoom-to-area */
1228 press1_valid = FALSE;
1229 xdelta = (int)(press1_event.x - event->x);
1233 /* is the mouse more or less where it started? */
1235 /* Control-left-mouse => sink the track */
1236 /* Shift-left-mouse => raise the track */
1237 if ((press1_event.state & GDK_CONTROL_MASK) ==
1239 move_current_track(event, s_v1, MOVE_BOTTOM);
1240 } else if ((press1_event.state & GDK_SHIFT_MASK) ==
1242 move_current_track(event, s_v1, MOVE_TOP);
1244 /* No modifiers: toggle the event */
1245 toggle_event_select(event, s_v1);
1247 /* Repaint to get rid of the zoom bar */
1249 /* Fix the cursor and leave. No zoom */
1250 gdk_window_set_cursor (da->window, norm_cursor);
1251 zoom_bar_up = FALSE;
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;
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);
1268 case GDK_MOTION_NOTIFY:
1269 /* Button one followed by motion: draw zoom fence and fix cursor */
1271 /* Fence, cursor already set */
1275 xdelta = (int)(press1_event.x - event->x);
1279 /* Haven't moved enough to declare a zoom sequence yet */
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;
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,
1290 gdk_window_set_cursor(da->window, zi_cursor);
1297 gdk_window_set_cursor(da->window, zi_cursor);
1300 * Some filtration is needed on Solaris, or the server will hang
1302 if (event->time - last_truler_time < 75)
1305 last_truler_time = event->time;
1307 line((int)(press3_event.x), s_v1->pop_offset,
1308 (int)(press3_event.x), time_ax_y, LINE_DRAW_BLACK);
1310 xdelta = (int)(press3_event.x - event->x);
1314 time_per_pixel = ((double)(s_v1->maxvistime - s_v1->minvistime)) /
1315 ((double)(s_v1->total_width - s_v1->pid_ax_width));
1317 time_ax_y = s_v1->npids * s_v1->strip_height + s_v1->pid_ax_offset;
1319 line((int)(press3_event.x), s_v1->pop_offset,
1320 (int)(press3_event.x), time_ax_y, LINE_DRAW_BLACK);
1322 * Note: use a fixed-width format so it looks like we're
1323 * erasing and redrawing the box.
1325 nsec = ((double)xdelta)*time_per_pixel;
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);
1333 sprintf(tmpbuf, "%8.0f nsec", nsec);
1335 tbox(tmpbuf, (int)(press3_event.x), s_v1->pop_offset,
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);
1362 view1_display_when_idle();
1367 /****************************************************************************
1369 * Happens when the window manager resizes the viewer's main window.
1370 ****************************************************************************/
1373 configure_event (GtkWidget *widget, GdkEventConfigure *event)
1375 /* Toss the previous drawing area backing store pixmap */
1377 gdk_pixmap_unref(pm);
1379 /* Create a new pixmap, paint it */
1380 pm = gdk_pixmap_new(widget->window,
1381 widget->allocation.width,
1382 widget->allocation.height,
1384 gdk_draw_rectangle (pm,
1385 widget->style->white_gc,
1388 widget->allocation.width,
1389 widget->allocation.height);
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) /
1397 /* Schedule a repaint */
1398 view1_display_when_idle();
1402 /****************************************************************************
1404 * Use backing store to fix the screen.
1405 ****************************************************************************/
1406 static gint expose_event (GtkWidget *widget, GdkEventExpose *event)
1408 gdk_draw_pixmap(widget->window,
1409 widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
1411 event->area.x, event->area.y,
1412 event->area.x, event->area.y,
1413 event->area.width, event->area.height);
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 ****************************************************************************/
1424 boolean event_search_internal (void)
1430 boolean full_redisplay = FALSE;
1431 ulonglong current_width;
1434 /* No events yet? Act like the search worked, to avoid a loop */
1438 ep = (g_events + s_srchindex);
1439 ep->flags &= ~EVENT_FLAG_SEARCHRSLT;
1442 * Assume the user wants to search [plus or minus]
1443 * from where they are.
1446 if (ep->time < s_v1->minvistime)
1447 s_srchindex = find_event_index (s_v1->minvistime);
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;
1455 ep = (g_events + index);
1457 if (ep->code == s_srchcode) {
1460 s_srchindex = index;
1461 pid_index = ep->pid->pid_index;
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;
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;
1482 s_v1->minvistime = ep->time - ((current_width+1)/2);
1483 s_v1->maxvistime = ep->time + ((current_width+1)/2);
1485 recompute_hscrollbar();
1486 full_redisplay = TRUE;
1488 ep->flags |= EVENT_FLAG_SEARCHRSLT;
1489 full_redisplay = TRUE;
1492 if (!full_redisplay){
1495 time_per_pixel = dtime_per_pixel(s_v1);
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)) /
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);
1505 full_redisplay = TRUE;
1511 view1_display_when_idle();
1515 sprintf (tmpbuf, "Search for event %ld failed...\n", s_srchcode);
1516 message_line(tmpbuf);
1517 s_srchfail_up = TRUE;
1521 /****************************************************************************
1522 * event_search_callback
1523 ****************************************************************************/
1525 boolean event_search_callback (char *s)
1527 /* No events yet? Act like the search worked, to avoid a loop */
1531 s_srchcode = atol(s);
1533 if (s_srchcode == 0)
1536 return(event_search_internal());
1539 /****************************************************************************
1541 ****************************************************************************/
1543 static void event_search (void)
1545 modal_dialog ("Event Search: Please Enter Event Code",
1546 "Invalid: Please Reenter Event Code", NULL,
1547 event_search_callback);
1550 /****************************************************************************
1552 ****************************************************************************/
1553 static void init_track_colors(void)
1559 gboolean dont_care[g_npids];
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.
1570 if (s_color != NULL ) {
1571 gdk_colormap_free_colors(gtk_widget_get_colormap(da),
1573 memset(s_color, 0, sizeof(GdkColor) * g_npids);
1576 * First time through: allocate the array to hold the GCs.
1578 s_color = g_malloc(sizeof(GdkColor) * g_npids);
1582 * Go through and assign a color for each track.
1584 for (i = 0; i < g_npids; i++) {
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.
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.
1598 label_char = get_track_label(g_pids[i].pid_value);
1600 while (*label_char != '\0' && *label_char != '(') {
1601 hash = hash * 33 + *label_char++;
1603 hash += hash >> 5; /* even out the lower order bits a touch */
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.
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.
1619 RGB[0] = (hash & 0xff000000) >> 16;
1620 RGB[1] = (hash & 0x007f0000) >> 8;
1621 RGB[2] = (hash & 0x0000ff00);
1622 RGB[hash % 3] &= 0x1fff;
1625 GdkColor color = {0, RGB[0], RGB[1], RGB[2]};
1627 g_pids[i].color_index = i;
1632 * Actually allocate the colors in one bulk operation. We ignore the return
1635 gdk_colormap_alloc_colors(gtk_widget_get_colormap(da),
1636 s_color, g_npids, FALSE, TRUE, dont_care);
1640 /****************************************************************************
1642 * Reorder the pid_index fields so the viewer "chases" the last selected
1644 ****************************************************************************/
1646 static void chase_event_etc(enum chase_mode mode)
1648 pid_sort_t *psp, *new_pidvec;
1652 ulong code_to_chase;
1653 ulong datum_to_chase;
1658 if (!s_last_selected_event) {
1659 infobox("No selected event",
1660 "\nPlease select an event and try again...\n");
1664 /* Clear all index assignments */
1666 for (i = 0; i < g_npids; i++) {
1668 pp->pid_index = 0xFFFFFFFF;
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;
1677 new_pidvec = g_malloc(sizeof(pid_sort_t)*g_npids);
1680 if (srch_chase_dir == SRCH_CHASE_FORWARD) {
1681 if (ep >= g_events + g_nevents)
1691 if (ep->code == code_to_chase) {
1697 if (ep->datum == datum_to_chase) {
1703 if (ep->pid->pid_value == pid_to_chase) {
1709 infobox("BUG", "unknown mode in chase_event_etc\n");
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;
1720 if (pids_mapped == g_npids)
1724 if (srch_chase_dir == SRCH_CHASE_FORWARD)
1730 /* Pass 2, first-to-last, to collect stragglers */
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;
1740 if (pids_mapped == g_npids)
1746 if (pids_mapped != g_npids) {
1747 infobox("BUG", "\nDidn't map all pids in chase_event_etc\n");
1751 g_pids = new_pidvec;
1754 * The new g_pids vector contains the "chase" sort, so we revert
1755 * the pid_index mapping to an identity map
1759 for (i = 0; i < g_npids; i++) {
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();
1773 /****************************************************************************
1775 * Copy g_original_pids to g_pids, revert index mapping
1776 ****************************************************************************/
1777 static void unchase_event_etc(void)
1783 memcpy (g_pids, g_original_pids, sizeof(pid_sort_t)*g_npids);
1785 /* Fix the pid structure index mappings */
1788 for (i = 0; i < g_npids; i++) {
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();
1802 /****************************************************************************
1804 * To fit a reasonable-sized landscape mode plot onto letter-size paper,
1805 * scale everything by .75.
1806 ****************************************************************************/
1808 static void print_ps_header (v1_geometry_t *vp, char *filename)
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,
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");
1832 /****************************************************************************
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
1839 * Xr = x*cos(theta) - y*sin(theta);
1840 * Yr = x*sin(theta) + y*cos(theta);
1842 * If we let theta = 90, this reduces to
1846 * Translate back to the origin in X by adding Ymax, yielding
1848 ****************************************************************************/
1850 static inline int xrt(int x, int y)
1852 return (s_v1->total_height - y);
1855 static inline int yrt(int x, int y)
1860 /****************************************************************************
1861 * print_screen_callback
1862 ****************************************************************************/
1864 static boolean print_screen_callback(char *filename)
1866 s_printfp = fopen (filename, "wt");
1868 if (s_printfp == NULL)
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.
1878 s_print_offset = TBOX_PRINT_PLAIN - TBOX_DRAW_PLAIN;
1880 print_ps_header(s_v1, filename);
1882 display_pid_axis(s_v1);
1883 display_event_data(s_v1);
1884 display_time_axis(s_v1);
1890 /* For tactile feedback */
1891 view1_display_when_idle();
1895 /****************************************************************************
1896 * view1_button_click_callback
1897 ****************************************************************************/
1899 static void view1_button_click_callback(GtkButton *item, gpointer data)
1901 enum view1_button_click click = (enum view1_button_click) data;
1903 ulonglong event_incdec;
1904 ulonglong current_width;
1905 ulonglong zoom_delta;
1908 current_width = s_v1->maxvistime - s_v1->minvistime;
1909 event_incdec = (current_width) / 3;
1911 if (event_incdec == 0LL)
1914 zoom_delta = (s_v1->maxvistime - s_v1->minvistime) / 6;
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));
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));
1944 case CHASE_EVENT_BUTTON:
1945 chase_event_etc(CHASE_EVENT);
1948 case CHASE_DATUM_BUTTON:
1949 chase_event_etc(CHASE_DATUM);
1952 case CHASE_TRACK_BUTTON:
1953 chase_event_etc(CHASE_TRACK);
1956 case UNCHASE_BUTTON:
1957 unchase_event_etc();
1962 s_v1->minvistime = 0LL;
1963 s_v1->maxvistime = current_width;
1964 recompute_hscrollbar();
1968 s_v1->minvistime += zoom_delta;
1969 s_v1->maxvistime -= zoom_delta;
1970 recompute_hscrollbar();
1973 case SEARCH_AGAIN_BUTTON:
1975 event_search_internal();
1978 /* NOTE FALLTHROUGH */
1984 case ZOOMOUT_BUTTON:
1985 if (zoom_delta == 0LL)
1988 if (s_v1->minvistime >= zoom_delta) {
1989 s_v1->minvistime -= zoom_delta;
1990 s_v1->maxvistime += zoom_delta;
1992 s_v1->minvistime = 0;
1993 s_v1->maxvistime += zoom_delta*2;
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;
2001 recompute_hscrollbar();
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)
2010 recompute_hscrollbar();
2013 case MORE_TRACES_BUTTON:
2014 /* Reduce the strip height to fit more traces on screen */
2015 s_v1->strip_height -= 1;
2017 if (s_v1->strip_height < 1) {
2018 s_v1->strip_height = 1;
2021 /* Recalculate the number of strips on the screen */
2022 s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) /
2024 recompute_vscrollbar();
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;
2034 /* Recalculate the number of strips on the screen */
2035 s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) /
2037 recompute_vscrollbar();
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);
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);
2052 case SUMMARY_BUTTON:
2053 summary_mode = TRUE;
2054 gtk_widget_hide (s_view1_summary_button);
2055 gtk_widget_show (s_view1_nosummary_button);
2058 case NOSUMMARY_BUTTON:
2059 summary_mode = FALSE;
2060 gtk_widget_show (s_view1_summary_button);
2061 gtk_widget_hide (s_view1_nosummary_button);
2065 view1_display_when_idle();
2068 /****************************************************************************
2069 * view1_print_callback
2070 ****************************************************************************/
2072 void view1_print_callback (GtkToggleButton *notused, gpointer nu2)
2074 modal_dialog("Print Screen (PostScript format) to file:",
2075 "Invalid file: Print Screen to file:",
2076 "g2.ps", print_screen_callback);
2079 /****************************************************************************
2081 ****************************************************************************/
2083 static void view1_hscroll (GtkAdjustment *adj, GtkWidget *notused)
2085 ulonglong current_width;
2087 current_width = (s_v1->maxvistime - s_v1->minvistime);
2089 s_v1->minvistime = (ulonglong)(adj->value);
2090 s_v1->maxvistime = s_v1->minvistime + current_width;
2092 view1_display_when_idle();
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);
2104 /****************************************************************************
2106 ****************************************************************************/
2108 static void view1_vscroll (GtkAdjustment *adj, GtkWidget *notused)
2110 s_v1->first_pid_index = (int)adj->value;
2111 view1_display_when_idle();
2114 void set_pid_ax_width(int width)
2116 s_v1->pid_ax_width = width;
2117 view1_display_when_idle();
2120 /****************************************************************************
2122 ****************************************************************************/
2124 void view1_init(void)
2127 c_view1_draw_width = atol(getprop_default("drawbox_width", "700"));
2128 c_view1_draw_height = atol(getprop_default("drawbox_height", "400"));
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;
2141 s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) /
2144 s_v1->minvistime = 0;
2145 s_v1->maxvistime = 200;
2147 s_view1_vbox = gtk_vbox_new(FALSE, 5);
2149 s_view1_hbox = gtk_hbox_new(FALSE, 5);
2151 da = gtk_drawing_area_new();
2152 gtk_drawing_area_size(GTK_DRAWING_AREA(da), c_view1_draw_width,
2153 c_view1_draw_height);
2156 gtk_signal_connect (GTK_OBJECT (da), "motion_notify_event",
2157 (GtkSignalFunc) motion_notify_event, NULL);
2160 gtk_signal_connect (GTK_OBJECT (da), "expose_event",
2161 (GtkSignalFunc) expose_event, NULL);
2163 gtk_signal_connect (GTK_OBJECT(da),"configure_event",
2164 (GtkSignalFunc) configure_event, NULL);
2166 gtk_signal_connect (GTK_OBJECT (da), "button_press_event",
2167 (GtkSignalFunc) button_press_event, NULL);
2169 gtk_signal_connect (GTK_OBJECT (da), "button_release_event",
2170 (GtkSignalFunc) button_press_event, NULL);
2172 gtk_signal_connect (GTK_OBJECT (da), "motion_notify_event",
2173 (GtkSignalFunc) button_press_event, NULL);
2175 gtk_widget_set_events (da, GDK_BUTTON_PRESS_MASK
2176 | GDK_BUTTON_RELEASE_MASK | GDK_EXPOSURE_MASK
2177 | GDK_BUTTON_MOTION_MASK);
2180 gtk_box_pack_start(GTK_BOX(s_view1_hbox), da, TRUE, TRUE, 0);
2182 g_font = gdk_font_load ("8x13");
2183 if (g_font == NULL) {
2184 g_error("Couldn't load 8x13 font...\n");
2186 gdk_font_ref(g_font);
2189 s_view1_vmenubox = gtk_vbox_new(FALSE, 5);
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 */);
2198 s_view1_vscroll = gtk_vscrollbar_new (GTK_ADJUSTMENT(s_view1_vsadj));
2200 gtk_signal_connect (GTK_OBJECT (s_view1_vsadj), "value-changed",
2201 GTK_SIGNAL_FUNC (view1_vscroll),
2202 (gpointer)s_view1_vscroll);
2204 s_view1_topbutton = gtk_button_new_with_label("Top");
2205 s_view1_bottombutton = gtk_button_new_with_label("Bottom");
2207 gtk_signal_connect (GTK_OBJECT(s_view1_topbutton), "clicked",
2208 GTK_SIGNAL_FUNC(view1_button_click_callback),
2209 (gpointer) TOP_BUTTON);
2211 gtk_signal_connect (GTK_OBJECT(s_view1_bottombutton), "clicked",
2212 GTK_SIGNAL_FUNC(view1_button_click_callback),
2213 (gpointer) BOTTOM_BUTTON);
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);
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,
2233 gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_topbutton,
2236 gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_vscroll,
2239 gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_bottombutton,
2242 gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_more_traces_button,
2245 gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_less_traces_button,
2248 gtk_box_pack_start (GTK_BOX(s_view1_hbox), s_view1_vmenubox,
2251 /* Time axis menu */
2253 s_view1_hmenubox = gtk_hbox_new(FALSE, 5);
2255 s_view1_startbutton = gtk_button_new_with_label("Start");
2257 s_view1_zoominbutton = gtk_button_new_with_label("ZoomIn");
2259 s_view1_searchbutton = gtk_button_new_with_label("Search");
2261 s_view1_srchagainbutton = gtk_button_new_with_label("Search Again");
2263 s_view1_zoomoutbutton = gtk_button_new_with_label("ZoomOut");
2265 s_view1_endbutton = gtk_button_new_with_label("End");
2267 gtk_signal_connect (GTK_OBJECT(s_view1_startbutton), "clicked",
2268 GTK_SIGNAL_FUNC(view1_button_click_callback),
2269 (gpointer) START_BUTTON);
2271 gtk_signal_connect (GTK_OBJECT(s_view1_zoominbutton), "clicked",
2272 GTK_SIGNAL_FUNC(view1_button_click_callback),
2273 (gpointer) ZOOMIN_BUTTON);
2275 gtk_signal_connect (GTK_OBJECT(s_view1_searchbutton), "clicked",
2276 GTK_SIGNAL_FUNC(view1_button_click_callback),
2277 (gpointer) SEARCH_BUTTON);
2279 gtk_signal_connect (GTK_OBJECT(s_view1_srchagainbutton), "clicked",
2280 GTK_SIGNAL_FUNC(view1_button_click_callback),
2281 (gpointer) SEARCH_AGAIN_BUTTON);
2283 gtk_signal_connect (GTK_OBJECT(s_view1_zoomoutbutton), "clicked",
2284 GTK_SIGNAL_FUNC(view1_button_click_callback),
2285 (gpointer) ZOOMOUT_BUTTON);
2287 gtk_signal_connect (GTK_OBJECT(s_view1_endbutton), "clicked",
2288 GTK_SIGNAL_FUNC(view1_button_click_callback),
2289 (gpointer) END_BUTTON);
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 */);
2298 s_view1_hscroll = gtk_hscrollbar_new (GTK_ADJUSTMENT(s_view1_hsadj));
2300 gtk_signal_connect (GTK_OBJECT (s_view1_hsadj), "value-changed",
2301 GTK_SIGNAL_FUNC (view1_hscroll),
2302 (gpointer)s_view1_hscroll);
2304 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_startbutton,
2307 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_hscroll,
2310 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_endbutton,
2313 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_zoominbutton,
2316 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_searchbutton,
2319 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_srchagainbutton,
2322 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_zoomoutbutton,
2325 gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_hbox,
2328 gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_hmenubox,
2332 s_view1_hmenubox2 = gtk_hbox_new(FALSE, 5);
2334 s_view1_snapbutton = gtk_button_new_with_label("Snap");
2336 s_view1_nextbutton = gtk_button_new_with_label("Next");
2338 s_view1_delbutton = gtk_button_new_with_label("Del");
2340 s_view1_chase_event_button = gtk_button_new_with_label("ChaseEvent");
2342 s_view1_chase_datum_button = gtk_button_new_with_label("ChaseDatum");
2344 s_view1_chase_track_button = gtk_button_new_with_label("ChaseTrack");
2346 s_view1_unchasebutton = gtk_button_new_with_label("NoChase");
2348 s_view1_forward_button = gtk_button_new_with_label("->SrchChase(is<-)");
2349 s_view1_backward_button = gtk_button_new_with_label("<-SrchChase(is->)");
2351 s_view1_summary_button = gtk_button_new_with_label("Summary");
2352 s_view1_nosummary_button = gtk_button_new_with_label("NoSummary");
2354 gtk_signal_connect (GTK_OBJECT(s_view1_snapbutton), "clicked",
2355 GTK_SIGNAL_FUNC(view1_button_click_callback),
2356 (gpointer) SNAP_BUTTON);
2358 gtk_signal_connect (GTK_OBJECT(s_view1_nextbutton), "clicked",
2359 GTK_SIGNAL_FUNC(view1_button_click_callback),
2360 (gpointer) NEXT_BUTTON);
2362 gtk_signal_connect (GTK_OBJECT(s_view1_delbutton), "clicked",
2363 GTK_SIGNAL_FUNC(view1_button_click_callback),
2364 (gpointer) DEL_BUTTON);
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);
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);
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);
2378 gtk_signal_connect (GTK_OBJECT(s_view1_unchasebutton), "clicked",
2379 GTK_SIGNAL_FUNC(view1_button_click_callback),
2380 (gpointer) UNCHASE_BUTTON);
2382 gtk_signal_connect (GTK_OBJECT(s_view1_forward_button), "clicked",
2383 GTK_SIGNAL_FUNC(view1_button_click_callback),
2384 (gpointer) FORWARD_BUTTON);
2386 gtk_signal_connect (GTK_OBJECT(s_view1_backward_button), "clicked",
2387 GTK_SIGNAL_FUNC(view1_button_click_callback),
2388 (gpointer) BACKWARD_BUTTON);
2390 gtk_signal_connect (GTK_OBJECT(s_view1_summary_button), "clicked",
2391 GTK_SIGNAL_FUNC(view1_button_click_callback),
2392 (gpointer) SUMMARY_BUTTON);
2394 gtk_signal_connect (GTK_OBJECT(s_view1_nosummary_button), "clicked",
2395 GTK_SIGNAL_FUNC(view1_button_click_callback),
2396 (gpointer) NOSUMMARY_BUTTON);
2398 gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_hmenubox2,
2401 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_snapbutton,
2404 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_nextbutton,
2407 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_delbutton,
2410 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_chase_event_button,
2413 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_chase_datum_button,
2416 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_chase_track_button,
2419 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_unchasebutton,
2422 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_forward_button,
2425 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_backward_button,
2428 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_summary_button,
2431 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_nosummary_button,
2434 s_view1_label = gtk_label_new(NULL);
2436 gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_label,
2439 gtk_box_pack_start (GTK_BOX(g_mainhbox), s_view1_vbox,
2442 gtk_widget_show_all (s_view1_vbox);
2443 GTK_WIDGET_SET_FLAGS(da, GTK_CAN_FOCUS);
2444 gtk_widget_grab_focus(da);
2446 gtk_widget_hide (s_view1_forward_button);
2447 gtk_widget_hide (summary_mode ? s_view1_summary_button
2448 : s_view1_nosummary_button);
2450 zi_source = gdk_bitmap_create_from_data (NULL, (char *)zi_bits, zi_width,
2452 zi_mask = gdk_bitmap_create_from_data (NULL, (char *)zi_bkgd, zi_width,
2455 zi_cursor = (GdkCursor *) gdk_cursor_new_from_pixmap (zi_source,
2457 &bg_white, zi_x_hot,
2459 gdk_pixmap_unref (zi_source);
2460 gdk_pixmap_unref (zi_mask);
2462 norm_cursor = (GdkCursor *) gdk_cursor_new (GDK_TOP_LEFT_ARROW);
2465 /****************************************************************************
2467 ****************************************************************************/
2469 void line_print (int x1, int y1, int x2, int y2)
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));
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");
2481 /****************************************************************************
2483 ****************************************************************************/
2484 GdkRectangle *tbox_print (char *s, int x, int y, enum view1_tbox_fn function,
2487 if (function == TBOX_PRINT_BOXED) {
2491 if ((function == TBOX_PRINT_BOXED) ||
2492 (function == TBOX_PRINT_EVENT)) {
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));
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));
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)));
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)));
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));
2516 fprintf(s_printfp, "stroke\n");
2519 if ((function == TBOX_PRINT_BOXED) ||
2520 (function == TBOX_PRINT_PLAIN)) {
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");
2535 /****************************************************************************
2536 * tbox - draws an optionally boxed string whose lower lefthand
2537 * corner is at (x, y). As usual, Y is backwards.
2538 ****************************************************************************/
2540 GdkRectangle *tbox (char *s, int x, int y, enum view1_tbox_fn function)
2542 static GdkRectangle update_rect;
2543 gint lbearing, rbearing, width, ascent, descent;
2545 gdk_string_extents (g_font, s,
2546 &lbearing, &rbearing,
2547 &width, &ascent, &descent);
2550 * If we have enough room to display full size events, then just
2551 * use the BOXED function instead of the EVENT function.
2553 if (s_v1->strip_height > 9) {
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;
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);
2570 gdk_draw_rectangle (pm, da->style->black_gc, FALSE,
2571 x, y - (ascent+descent+3), width + 2,
2572 ascent + descent + 3);
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:
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);
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,
2590 /* NOTE FALLTHROUGH */
2591 case TBOX_GETRECT_EVENT:
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);
2601 case TBOX_DRAW_PLAIN:
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:
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);
2615 case TBOX_PRINT_BOXED:
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));
2624 case TBOX_PRINT_EVENT:
2625 /* We have a small event box to print...no text */
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));
2632 return(&update_rect);
2635 /****************************************************************************
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.
2642 * line_batch_mode contains the state for this:
2644 * BATCH_OFF: no batching, update for every line
2645 * BATCH_NEW: just entered a batch, so initialize the area to update from
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 ****************************************************************************/
2652 static enum { BATCH_OFF, BATCH_NEW, BATCH_EXISTING } line_batch_mode;
2653 static int line_batch_count;
2654 static int line_minx, line_miny, line_maxx, line_maxy;
2656 void line_batch_start (void)
2658 line_batch_mode = BATCH_NEW;
2659 line_batch_count = 0;
2662 void line_batch_end (void)
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);
2672 line_batch_mode = BATCH_OFF;
2675 void line (int x1, int y1, int x2, int y2, enum view1_line_fn function)
2677 GdkRectangle update_rect;
2681 case LINE_DRAW_BLACK:
2682 gc = da->style->black_gc;
2685 case LINE_DRAW_WHITE:
2686 gc = da->style->white_gc;
2690 line_print (x1, y1, x2, y2);
2694 gdk_draw_line (pm, gc, x1, y1, x2, y2);
2696 switch (line_batch_mode) {
2700 update_rect.width = (x2-x1) + 1;
2701 update_rect.height = (y2-y1) + 1;
2702 gtk_widget_draw (da, &update_rect);
2710 line_batch_mode = BATCH_EXISTING;
2711 line_batch_count = 1;
2714 case BATCH_EXISTING:
2729 /****************************************************************************
2731 ****************************************************************************/
2733 static void display_pid_axis(v1_geometry_t *vp)
2735 int y, i, label_tick;
2736 int last_printed_y = -vp->strip_height;
2742 /* No pids yet? Outta here */
2748 for (i = 0; i < vp->npids; i++) {
2749 pid_index = vp->first_pid_index + i;
2750 if (pid_index >= g_npids)
2753 set_color(pid_index);
2754 pp = (g_pids + pid_index);
2756 label_fmt = get_track_label(pp->pid_value);
2757 snprintf(tmpbuf, sizeof(tmpbuf)-1, label_fmt, pp->pid_value);
2759 y = i*vp->strip_height + vp->pid_ax_offset;
2762 * Have we incremented enough space to have another label not
2763 * overlap the previous label?
2765 if (y - last_printed_y > 9) {
2767 tbox(tmpbuf, 0, y +4, TBOX_DRAW_PLAIN+s_print_offset);
2772 * And let the line stick out a bit more to indicate this label
2773 * relates to the following line.
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);
2789 set_color(COLOR_DEFAULT);
2793 /****************************************************************************
2794 * view1_read_events_callback
2795 * New event data just showed up, reset a few things.
2796 ****************************************************************************/
2798 void view1_read_events_callback(void)
2802 s_v1->first_pid_index = 0;
2804 max_vis_index = 300;
2805 if (max_vis_index > g_nevents)
2806 max_vis_index = g_nevents-1;
2808 s_v1->minvistime = 0LL;
2809 s_v1->maxvistime = (g_events[g_nevents - 1].time * 9)/ 8;
2812 s_last_selected_event = 0;
2814 init_track_colors();
2816 recompute_hscrollbar();
2817 recompute_vscrollbar();
2820 /****************************************************************************
2821 * display_event_data
2822 ****************************************************************************/
2824 static void display_event_data(v1_geometry_t *vp)
2831 double time_per_pixel;
2833 GdkRectangle *print_rect;
2836 /* Happens if one loads the event def header first, for example. */
2840 time_per_pixel = dtime_per_pixel(vp);
2842 start_index = find_event_index (vp->minvistime);
2844 /* Scrolled too far right? */
2845 if (start_index >= g_nevents)
2848 ep = (g_events + start_index);
2850 if (s_print_offset || summary_mode) {
2851 last_x_used = (int *)g_malloc0(vp->npids * sizeof(int));
2858 while (ep < (g_events + g_nevents) &&
2859 (ep->time < vp->maxvistime)) {
2860 pid_index = ep->pid->pid_index;
2861 set_color(pid_index);
2863 /* First filter: pid out of range */
2864 if ((pid_index < vp->first_pid_index) ||
2865 (pid_index >= vp->first_pid_index + vp->npids)) {
2870 /* Second filter: event hidden */
2871 edp = find_event_definition (ep->code);
2872 if (!edp->selected) {
2879 pid_index -= vp->first_pid_index;
2881 y = pid_index*vp->strip_height + vp->event_offset;
2883 x = vp->pid_ax_width +
2884 (int)(((double)(ep->time - vp->minvistime)) / time_per_pixel);
2886 if (last_x_used != NULL && x < last_x_used[pid_index]) {
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);
2895 sprintf(tmpbuf, edp->name);
2896 sprintf(tmpbuf+strlen(tmpbuf), ": ");
2897 sprintf(tmpbuf+strlen(tmpbuf), edp->format, ep->datum);
2900 sprintf(tmpbuf, "SEARCH RESULT");
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;
2909 int delta = vp->strip_height / 3;
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;
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;
2925 g_free(last_x_used);
2927 set_color(COLOR_DEFAULT);
2930 /****************************************************************************
2932 ****************************************************************************/
2934 static void display_clear(void)
2936 GdkRectangle update_rect;
2938 gdk_draw_rectangle (pm, da->style->white_gc, TRUE,
2939 0, 0, da->allocation.width,
2940 da->allocation.height);
2944 update_rect.width = da->allocation.width;
2945 update_rect.height = da->allocation.height;
2947 gtk_widget_draw (da, &update_rect);
2950 /****************************************************************************
2952 ****************************************************************************/
2954 static void display_time_axis(v1_geometry_t *vp)
2957 int xoffset, nticks;
2959 double unit_divisor;
2962 double time_per_pixel;
2964 y = vp->npids * vp->strip_height + vp->pid_ax_offset;
2966 x = vp->pid_ax_width;
2968 nticks = (vp->total_width - vp->pid_ax_width) / vp->time_ax_spacing;
2970 time_per_pixel = dtime_per_pixel(vp);
2973 unit_divisor = 1.00;
2975 if ((vp->maxvistime / unit_divisor) > 1000) {
2977 unit_divisor = 1000.00;
2980 if ((vp->maxvistime / unit_divisor) > 1000) {
2982 unit_divisor = 1000.00*1000.00;
2984 if ((vp->maxvistime / unit_divisor) > 1000) {
2986 unit_divisor = 1000.00*1000.00*1000.00;
2990 line(x, y, vp->total_width, y, LINE_DRAW_BLACK+s_print_offset);
2994 for (i = 0; i < nticks; i++) {
2996 line(x+xoffset, y-3, x+xoffset, y+3, LINE_DRAW_BLACK+s_print_offset);
2998 time = (double)(x + xoffset - vp->pid_ax_width);
2999 time *= time_per_pixel;
3000 time += (double)(vp->minvistime);
3001 time /= unit_divisor;
3003 sprintf (tmpbuf, "%.2f%s", time, units);
3005 tbox(tmpbuf, x+xoffset, y+15, TBOX_DRAW_PLAIN+s_print_offset);
3007 xoffset += vp->time_ax_spacing;
3011 /****************************************************************************
3013 * Forget about any temporary displays, they're gone now...
3014 ****************************************************************************/
3016 static void clear_scoreboard(void)
3018 s_result_up = FALSE;
3021 /****************************************************************************
3023 ****************************************************************************/
3025 void view1_display(void)
3028 display_pid_axis(s_v1);
3029 display_event_data(s_v1);
3030 display_time_axis(s_v1);
3034 static gint idle_tag;
3036 /****************************************************************************
3037 * view1_display_eventually
3038 ****************************************************************************/
3040 static void view1_display_eventually(void)
3042 gtk_idle_remove(idle_tag);
3048 /****************************************************************************
3049 * view1_display_when_idle
3050 ****************************************************************************/
3052 void view1_display_when_idle(void)
3054 if (idle_tag == 0) {
3055 idle_tag = gtk_idle_add((GtkFunction) view1_display_eventually, 0);
3059 /****************************************************************************
3061 ****************************************************************************/
3063 void view1_about (char *tmpbuf)
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);
3073 for (nsnaps = 0, snaps = s_snapshots; snaps; snaps = snaps->next) {
3076 sprintf(tmpbuf+strlen(tmpbuf), "%d snapshots in the ring\n", nsnaps);