blob: 38c41522a4523ec57106896d730dc19f454e6c00 [file] [log] [blame]
Dave Barach9c8cfd32019-02-03 10:44:47 -05001/*
Dave Barach52642c32016-02-11 19:28:19 -05002 *------------------------------------------------------------------
Dave Barach9c8cfd32019-02-03 10:44:47 -05003 * Copyright (c) 2005-2019 Cisco and/or its affiliates.
Dave Barach52642c32016-02-11 19:28:19 -05004 * 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>
Dave Barach9c8cfd32019-02-03 10:44:47 -050027#include <math.h>
Dave Barach52642c32016-02-11 19:28:19 -050028
29/*
30 * The main event display view.
Dave Barach9c8cfd32019-02-03 10:44:47 -050031 *
Dave Barach52642c32016-02-11 19:28:19 -050032 * Important variables:
33 *
34 * "da" -- the drawing area, aka the screen representation of the
35 * event view.
36 *
37 * "pm" -- the backing pixmap for the drawing area. Note that
38 * all graphics operations target this backing
39 * store, then call gtk_widget_draw to copy a rectangle from
40 * the backing store onto the screen.
41 *
42 * "s_v1" -- pointer to the current v1_geometry_t object.
Dave Barach9c8cfd32019-02-03 10:44:47 -050043 *
Dave Barach52642c32016-02-11 19:28:19 -050044 * Box heirarchy:
45 * s_view1_vbox
46 * s_view1_hbox
47 * da s_view1_vmenubox
48 * s_view1_topbutton("Top")
49 * s_view1_vscroll (vertical scrollbar)
50 * s_view1_bottombutton("Bottom")
51 * s_view1_hmenubox
52 * s_view1_startbutton("Start");
53 * s_view1_hscroll(horizontal scrollbar)
54 * s_view1_endbutton("End")
55 * s_view1_zoominbutton("Zoomin")
56 * s_view1_searchbutton("Search")
57 * s_view1_searchagainbutton("Search Again")
58 * s_view1_zoomoutbutton("Zoomout")
59 * s_view1_label
60 */
61
62/*
63 * Globals
64 */
65
66GdkFont *g_font; /* a fixed-width font to use */
Dave Barach2c35e582017-04-03 10:22:17 -040067/* color format: 0 (for static colors), r (0-64k), g (0-64k), b (0-64k) */
Dave Barach52642c32016-02-11 19:28:19 -050068GdkColor fg_black = {0, 0, 0, 0};
Dave Barach2c35e582017-04-03 10:22:17 -040069GdkColor fg_red = {0, 65535, 0, 0};
Dave Barach52642c32016-02-11 19:28:19 -050070GdkColor bg_white = {0, 65535, 65535, 65535};
71static boolean summary_mode = TRUE; /* start out in summary mode */
Dave Barach2c35e582017-04-03 10:22:17 -040072static boolean color_mode = FALSE; /* start out in monochrome mode */
Dave Barach52642c32016-02-11 19:28:19 -050073
74/*
75 * Locals
76 */
77
Dave Barach9c8cfd32019-02-03 10:44:47 -050078/*
Dave Barach52642c32016-02-11 19:28:19 -050079 * user_data values passed to view1_button_click_callback,
80 * which is used by the various action buttons noted above
81 */
82enum view1_button_click {
83 TOP_BUTTON=1,
84 BOTTOM_BUTTON,
85 START_BUTTON,
86 ZOOMIN_BUTTON,
87 SEARCH_BUTTON,
Dave Barach9c8cfd32019-02-03 10:44:47 -050088 ANOMALY_BUTTON,
89 ANOMALY_NEXT_BUTTON,
90 ANOMALY_THRESHOLD_BUTTON,
Dave Barach52642c32016-02-11 19:28:19 -050091 SEARCH_AGAIN_BUTTON,
92 ZOOMOUT_BUTTON,
93 END_BUTTON,
94 MORE_TRACES_BUTTON,
95 LESS_TRACES_BUTTON,
96 SNAP_BUTTON,
97 NEXT_BUTTON,
98 DEL_BUTTON,
99 CHASE_EVENT_BUTTON,
100 CHASE_DATUM_BUTTON,
101 CHASE_TRACK_BUTTON,
102 UNCHASE_BUTTON,
103 FORWARD_BUTTON,
104 BACKWARD_BUTTON,
105 SUMMARY_BUTTON,
106 NOSUMMARY_BUTTON,
Dave Barach2c35e582017-04-03 10:22:17 -0400107 SLEW_LEFT_BUTTON,
108 SLEW_RIGHT_BUTTON,
Dave Barach52642c32016-02-11 19:28:19 -0500109};
110
111enum chase_mode {
112 CHASE_EVENT=1,
113 CHASE_DATUM,
114 CHASE_TRACK,
115};
116
117enum sc_dir {
118 SRCH_CHASE_FORWARD = 0,
119 SRCH_CHASE_BACKWARD = 1,
120};
121
122static GtkWidget *s_view1_hbox; /* see box heirarchy chart */
123static GtkWidget *s_view1_vbox; /* see box heirarchy chart */
124static GtkWidget *da; /* main drawing area */
125static GdkPixmap *pm; /* and its backing pixmap */
126static GdkCursor *norm_cursor; /* the "normal" cursor */
127
128/*
129 * view geometry parameters
130 *
131 * Remember:
132 * Y increases down the page.
133 * Strip origin is at the top
134 * Payday is Friday
135 * Don't put your fingers in your mouth.
136 *
137 * Most of these values are in pixels
138 */
139
140typedef struct v1_geometry {
141 int pid_ax_width; /* Width of the PID axis */
142 int time_ax_height; /* Height of the time axis */
143 int time_ax_spacing; /* TimeAxis: Space between tick-marks */
144 int strip_height; /* Height of a regular PID trace */
145 int pop_offset; /* Vertical offset of the detail box */
146 int pid_ax_offset; /* Vertical offset of the PID axis */
147 int event_offset; /* Vertical offset of the event boxes */
148 int total_height; /* total height of da, see configure_event */
149 int total_width; /* ditto, for width */
Dave Barach2c35e582017-04-03 10:22:17 -0400150 double last_time_interval; /* last time interval, in f64 seconds */
Dave Barach9c8cfd32019-02-03 10:44:47 -0500151 double anomaly_threshold_stddevs; /* Anomaly detection threshold */
152
Dave Barach52642c32016-02-11 19:28:19 -0500153 /* Derived values */
154 int first_pid_index; /* Index of first displayed PID */
155 int npids; /* Max number of displayed pids */
156 ulonglong minvistime; /* in usec */
157 ulonglong maxvistime; /* in usec */
Dave Barach9c8cfd32019-02-03 10:44:47 -0500158
159 /* Anomaly detection statistics */
160 f64 *means, *variances, *two_stddevs;
Dave Barach3117ad82019-02-04 17:41:29 -0500161 f64 *mins, *maxes;
Dave Barach9c8cfd32019-02-03 10:44:47 -0500162 u32 *matches;
163
Dave Barach52642c32016-02-11 19:28:19 -0500164} v1_geometry_t;
165
166
167/* The active geometry object */
Dave Barach9c8cfd32019-02-03 10:44:47 -0500168static v1_geometry_t s_v1record;
169static v1_geometry_t *s_v1 = &s_v1record;
Dave Barach52642c32016-02-11 19:28:19 -0500170
171/* The color array */
172static GdkColor *s_color;
173
174/* Snapshot ring */
175typedef struct snapshot {
176 struct snapshot *next;
177 /* Screen geometry */
178 v1_geometry_t geometry;
179 boolean show_event[NEVENTS];
180 pid_sort_t *pidvec;
181 /*
182 * Note: not worth recomputing the vertical scrollbar, just save
183 * its value here
184 */
185 gfloat vscroll_value;
186 boolean summary_mode;
187 boolean color_mode;
188} snapshot_t;
189
190static snapshot_t *s_snapshots;
191static snapshot_t *s_cursnap;
192static event_t *s_last_selected_event;
193
194/*
195 * various widgets, see the box heirarchy chart above
Dave Barach9c8cfd32019-02-03 10:44:47 -0500196 * The toolkit keeps track of these things, we could lose many of
197 * these pointers.
Dave Barach52642c32016-02-11 19:28:19 -0500198 */
199static GtkWidget *s_view1_vmenubox;
200static GtkWidget *s_view1_topbutton;
201static GtkWidget *s_view1_bottombutton;
202static GtkWidget *s_view1_more_traces_button;
203static GtkWidget *s_view1_less_traces_button;
204
205static GtkWidget *s_view1_hmenubox;
206static GtkWidget *s_view1_hmenubox2;
207static GtkWidget *s_view1_startbutton;
208static GtkWidget *s_view1_zoominbutton;
209static GtkWidget *s_view1_searchbutton;
210static GtkWidget *s_view1_srchagainbutton;
Dave Barach9c8cfd32019-02-03 10:44:47 -0500211static GtkWidget *s_view1_anomalybutton;
212static GtkWidget *s_view1_anomalynextbutton;
Dave Barach52642c32016-02-11 19:28:19 -0500213static GtkWidget *s_view1_zoomoutbutton;
214static GtkWidget *s_view1_endbutton;
215
216static GtkWidget *s_view1_snapbutton;
217static GtkWidget *s_view1_nextbutton;
218static GtkWidget *s_view1_delbutton;
219
220static GtkWidget *s_view1_chase_event_button;
221static GtkWidget *s_view1_chase_datum_button;
222static GtkWidget *s_view1_chase_track_button;
223static GtkWidget *s_view1_unchasebutton;
224
225static GtkWidget *s_view1_forward_button;
226static GtkWidget *s_view1_backward_button;
227
228static GtkWidget *s_view1_summary_button;
229static GtkWidget *s_view1_nosummary_button;
230
Dave Barach2c35e582017-04-03 10:22:17 -0400231static GtkWidget *s_view1_time_slew_right_button;
232static GtkWidget *s_view1_time_slew_left_button;
233
Dave Barach9c8cfd32019-02-03 10:44:47 -0500234static GtkWidget *s_view1_anomalythresholdbutton;
235
Dave Barach52642c32016-02-11 19:28:19 -0500236static GtkWidget *s_view1_hscroll;
237static GtkObject *s_view1_hsadj;
238
239static GtkWidget *s_view1_vscroll;
240static GtkObject *s_view1_vsadj;
241
242static GtkWidget *s_view1_label;
243
244/*
Dave Barach9c8cfd32019-02-03 10:44:47 -0500245 * Search context
Dave Barach52642c32016-02-11 19:28:19 -0500246 */
247static ulong s_srchcode; /* search event code */
Dave Barach9c8cfd32019-02-03 10:44:47 -0500248static ulong s_anomalycode; /* anomaly event code */
Dave Barach52642c32016-02-11 19:28:19 -0500249static int s_srchindex; /* last hit was at this event index */
250static boolean s_result_up; /* The SEARCH RESULT dongle is displayed */
251static boolean s_srchfail_up; /* The status line "Search Failed" is up */
252static int srch_chase_dir; /* search/chase dir, 0=>forward */
253
254
255/*
Dave Barach9c8cfd32019-02-03 10:44:47 -0500256 * Print context
Dave Barach52642c32016-02-11 19:28:19 -0500257 */
258static int s_print_offset; /* Magic offset added to line, tbox fn codes */
Dave Barach9c8cfd32019-02-03 10:44:47 -0500259static FILE *s_printfp;
Dave Barach52642c32016-02-11 19:28:19 -0500260
261/*
262 * Forward reference prototypes
263 */
264static void display_pid_axis(v1_geometry_t *vp);
265static void display_event_data(v1_geometry_t *vp);
266static void display_time_axis(v1_geometry_t *vp);
267static void view1_button_click_callback(GtkButton *item, gpointer data);
268
269/*
270 * config params
271 */
272
273gint c_view1_draw_width;
274gint c_view1_draw_height;
275
276/*
277 * Zoom-In / Time Ruler cursor
278 */
279
280#define zi_width 32
281#define zi_height 32
282#define zi_x_hot 22
283#define zi_y_hot 14
284static unsigned char zi_bits[] = {
285 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
286 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
287 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
288 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x88, 0x00,
289 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xc0, 0x00,
290 0x00, 0xfc, 0xff, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xa0, 0x00,
291 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x84, 0x00,
292 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
293 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
294 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
295 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
296
297static unsigned char zi_bkgd[] = {
298 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
299 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
300 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
301 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x88, 0x00,
302 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xc0, 0x00,
303 0x00, 0xfc, 0xff, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xa0, 0x00,
304 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x84, 0x00,
305 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
306 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
307 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
308 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
309
310static GdkCursor *zi_cursor;
311static GdkPixmap *zi_source, *zi_mask;
312
Dave Barach9c8cfd32019-02-03 10:44:47 -0500313/*
Dave Barach52642c32016-02-11 19:28:19 -0500314 * Frequently-used small computations, best
315 * done correctly once and instantiated.
316 */
317
318/****************************************************************************
319* dtime_per_pixel
320****************************************************************************/
321
322static inline double dtime_per_pixel(v1_geometry_t *vp)
323{
324 return ((double)(vp->maxvistime - vp->minvistime)) /
325 ((double)(vp->total_width - vp->pid_ax_width));
326}
327
328/****************************************************************************
329* message_line
330* Changes the status line. Pass "" to clear the status line.
331****************************************************************************/
332
333void message_line (char *s)
334{
335 gtk_label_set_text (GTK_LABEL(s_view1_label), s);
336}
337
338/****************************************************************************
339* set_window_title
340* Changes the window title to include the specified filename.
341****************************************************************************/
342
343void set_window_title (const char *filename)
344{
345 char title[128];
346 snprintf(title, sizeof(title), "g2 (%s)", filename);
347 gtk_window_set_title(GTK_WINDOW(g_mainwindow), title);
348}
349
350/****************************************************************************
351* recompute_hscrollbar
352* Adjust the horizontal scrollbar's adjustment object.
Dave Barach9c8cfd32019-02-03 10:44:47 -0500353*
Dave Barach52642c32016-02-11 19:28:19 -0500354* GtkAdjustments are really cool, but have to be set up exactly
355* right or the various client objects screw up completely.
356*
357* Note: this function is *not* called when the user clicks the scrollbar.
358****************************************************************************/
359
360static void recompute_hscrollbar (void)
361{
362 ulonglong current_width;
363 ulonglong event_incdec;
364 GtkAdjustment *adj;
365 event_t *ep;
366
367 if (g_nevents == 0)
368 return;
369
370 ep = (g_events + (g_nevents-1));
371 current_width = s_v1->maxvistime - s_v1->minvistime;
372 event_incdec = (current_width) / 6;
373
374 adj = GTK_ADJUSTMENT(s_view1_hsadj);
375
Dave Barach9c8cfd32019-02-03 10:44:47 -0500376 /*
Dave Barach52642c32016-02-11 19:28:19 -0500377 * Structure member decoder ring
378 * -----------------------------
379 * lower the minimum possible value
380 * value the current value
381 * upper the maximum possible value
382 * step_increment end button click increment
383 * page_increment click in trough increment
384 * page_size size of currently visible area
385 */
386
Dave Barach9c8cfd32019-02-03 10:44:47 -0500387 adj->lower = (gfloat)0.00;
Dave Barach52642c32016-02-11 19:28:19 -0500388 adj->value = (gfloat)s_v1->minvistime;
389
390 /* Minor click: move about 1/6 of a page */
391 adj->step_increment = (gfloat)event_incdec;
392
393 /* Major click: move about 1/3 of a page. */
394 adj->page_increment = (gfloat)(2*event_incdec);
395
396 /* allow the user to go a bit past the end */
397 adj->upper = adj->page_increment/3 + (gfloat)(ep->time);
398 adj->page_size = (gfloat)(current_width);
399
400 /*
Dave Barach9c8cfd32019-02-03 10:44:47 -0500401 * Tell all clients (e.g. the visible scrollbar) to
402 * make themselves look right
Dave Barach52642c32016-02-11 19:28:19 -0500403 */
404 gtk_adjustment_changed(adj);
405 gtk_adjustment_value_changed(adj);
406}
407
408/****************************************************************************
409* recompute_vscrollbar
410* Ditto, for the vertical scrollbar
411****************************************************************************/
412
413static void recompute_vscrollbar (void)
414{
415 GtkAdjustment *adj;
416
417 adj = GTK_ADJUSTMENT(s_view1_vsadj);
418
419 adj->lower = (gfloat)0.00;
420 adj->upper = (gfloat)g_npids;
421 adj->value = (gfloat)0.00;
422 adj->step_increment = 1.00;
423 adj->page_increment = (gfloat)(s_v1->npids / 3);
424 adj->page_size = (gfloat)s_v1->npids;
425 gtk_adjustment_changed(adj);
426 gtk_adjustment_value_changed(adj);
427}
428
429/****************************************************************************
430* format_popbox_string
431****************************************************************************/
432
433elog_main_t elog_main;
434
435void format_popbox_string (char *tmpbuf, int len, event_t *ep, event_def_t *edp)
436{
437 char *fp;
438
Dave Barach52642c32016-02-11 19:28:19 -0500439 if (ep->flags & EVENT_FLAG_CLIB) {
440 elog_event_t *eep;
441 u8 *s;
442
443 eep = get_clib_event (ep->datum);
Dave Barach9c8cfd32019-02-03 10:44:47 -0500444
Dave Barach52642c32016-02-11 19:28:19 -0500445 s = format (0, "%U", format_elog_event, &elog_main, eep);
446 memcpy (tmpbuf, s, vec_len(s));
447 tmpbuf[vec_len(s)] = 0;
448 vec_free(s);
449 return;
450 }
451
452 snprintf(tmpbuf, len, "%s", edp->name);
453 fp = edp->format;
454 /* Make sure there's a real format string. If so, add it */
455 while (fp && *fp) {
456 if (*fp != ' ') {
457 snprintf(tmpbuf+strlen(tmpbuf), len - strlen(tmpbuf), ": ");
458 /* %s only supported for cpel files */
459 if (fp[1] == 's') {
Dave Barach9c8cfd32019-02-03 10:44:47 -0500460 snprintf(tmpbuf+strlen(tmpbuf), len - strlen(tmpbuf),
Dave Barach52642c32016-02-11 19:28:19 -0500461 edp->format, strtab_ref(ep->datum));
462 } else {
Dave Barach9c8cfd32019-02-03 10:44:47 -0500463 snprintf(tmpbuf+strlen(tmpbuf), len - strlen(tmpbuf),
Dave Barach52642c32016-02-11 19:28:19 -0500464 edp->format, ep->datum);
465 }
466 return;
467 }
468 fp++;
469 }
470}
471
472/****************************************************************************
473 * add_snapshot
474 ****************************************************************************/
475
476static void add_snapshot(void)
477{
478 int i;
479 snapshot_t *new = g_malloc(sizeof(snapshot_t));
480
481 memcpy(&new->geometry, s_v1, sizeof(new->geometry));
482 for (i = 0; i < NEVENTS; i++) {
483 new->show_event[i] = g_eventdefs[i].selected;
484 }
485 new->pidvec = g_malloc(sizeof(pid_sort_t)*g_npids);
486 memcpy(new->pidvec, g_pids, sizeof(pid_sort_t)*g_npids);
487 new->vscroll_value = GTK_ADJUSTMENT(s_view1_vsadj)->value;
488 new->summary_mode = summary_mode;
489 new->color_mode = color_mode;
490
491 if (s_snapshots) {
492 new->next = s_snapshots;
493 s_snapshots = new;
494 } else {
495 new->next = 0;
496 s_snapshots = new;
497 }
498 s_cursnap = new;
499}
500
501/****************************************************************************
502 * next_snapshot
503 ****************************************************************************/
504
505static void next_snapshot(void)
506{
507 snapshot_t *next;
508 int i;
509 pid_sort_t *psp;
510 pid_data_t *pp;
511
512 if (!s_snapshots) {
Dave Barach9c8cfd32019-02-03 10:44:47 -0500513 infobox("No snapshots", "\nNo snapshots in the ring...\n");
Dave Barach52642c32016-02-11 19:28:19 -0500514 return;
515 }
Dave Barach9c8cfd32019-02-03 10:44:47 -0500516
Dave Barach52642c32016-02-11 19:28:19 -0500517 next = s_cursnap->next;
518 if (next == 0)
519 next = s_snapshots;
520
521 s_cursnap = next;
522
523 memcpy(s_v1, &next->geometry, sizeof(next->geometry));
524 for (i = 0; i < NEVENTS; i++) {
525 g_eventdefs[i].selected = next->show_event[i];
526 }
527 memcpy(g_pids, next->pidvec, sizeof(pid_sort_t)*g_npids);
528 color_mode = next->color_mode;
529 /*
530 * Update summary mode via a button push so that the button state is
531 * updated accordingly. (Should ideally clean up the view/controller
532 * separation properly one day.)
533 */
534 if (summary_mode != next->summary_mode) {
535 view1_button_click_callback
536 (NULL, (gpointer)(unsigned long long)
537 (summary_mode ? NOSUMMARY_BUTTON : SUMMARY_BUTTON));
538 }
539
540 /* Fix the pid structure index mappings */
541 psp = g_pids;
542
543 for (i = 0; i < g_npids; i++) {
544 pp = psp->pid;
545 pp->pid_index = i;
546 psp++;
547 }
548 GTK_ADJUSTMENT(s_view1_vsadj)->value = next->vscroll_value;
549 gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
550 recompute_hscrollbar();
551 pointsel_next_snapshot();
552 view1_display_when_idle();
553}
554
555
556/****************************************************************************
557 * del_snapshot
558 ****************************************************************************/
559
560static void del_snapshot(void)
561{
562 snapshot_t *prev;
563 snapshot_t *this;
564
565 if (!s_snapshots) {
Dave Barach9c8cfd32019-02-03 10:44:47 -0500566 infobox("No snapshots", "\nNo snapshots to delete...\n");
Dave Barach52642c32016-02-11 19:28:19 -0500567 return;
568 }
569
570 prev = NULL;
571 this = s_snapshots;
572
573 while (this && this != s_cursnap) {
574 prev = this;
575 this = this->next;
576 }
577
578 if (this != s_cursnap) {
Dave Barach9c8cfd32019-02-03 10:44:47 -0500579 infobox("BUG", "\nSnapshot AWOL!\n");
Dave Barach52642c32016-02-11 19:28:19 -0500580 return;
581 }
Dave Barach9c8cfd32019-02-03 10:44:47 -0500582
Dave Barach52642c32016-02-11 19:28:19 -0500583 s_cursnap = this->next;
584
585 /* middle of the list? */
586 if (prev) {
587 prev->next = this->next;
588 g_free(this->pidvec);
589 g_free(this);
590 } else { /* start of the list */
591 s_snapshots = this->next;
592 g_free(this->pidvec);
593 g_free(this);
594 }
Dave Barach9c8cfd32019-02-03 10:44:47 -0500595
Dave Barach52642c32016-02-11 19:28:19 -0500596 /* Note: both will be NULL after last delete */
597 if (s_cursnap == NULL)
598 s_cursnap = s_snapshots;
599}
600
601/****************************************************************************
602 * write_snapshot
603 *
604 * VERY primitive right now - not endian or version independent, and only
605 * writes to "snapshots.g2" in the current directory
606 ****************************************************************************/
607static void write_snapshot(void)
608{
609 FILE *file = NULL;
610 snapshot_t *snap;
611 char *error = NULL;
612 int records = 0;
Dave Barach9c8cfd32019-02-03 10:44:47 -0500613
Dave Barach52642c32016-02-11 19:28:19 -0500614 if (s_snapshots == NULL) {
615 error = "No snapshots defined";
616 errno = 0;
617 }
618
619 if (!error) {
620 file = fopen("snapshots.g2", "w");
621 if (file == NULL) {
622 error = "Unable to open snapshots.g2";
623 }
624 }
625
626 /*
627 * Simply serialize the arch-dependent binary data, without a care in the
628 * world. Don't come running to me if you try to read it and crash.
629 */
630 for (snap = s_snapshots; !error && snap != NULL; snap = snap->next) {
Dave Barach9c8cfd32019-02-03 10:44:47 -0500631 if (fwrite(&snap->geometry,
Dave Barach52642c32016-02-11 19:28:19 -0500632 sizeof(snap->geometry), 1, file) != 1 ||
Dave Barach9c8cfd32019-02-03 10:44:47 -0500633 fwrite(&snap->show_event,
Dave Barach52642c32016-02-11 19:28:19 -0500634 sizeof(snap->show_event), 1, file) != 1 ||
Dave Barach9c8cfd32019-02-03 10:44:47 -0500635 fwrite(snap->pidvec,
Dave Barach52642c32016-02-11 19:28:19 -0500636 sizeof(pid_sort_t) * g_npids, 1, file) != 1 ||
Dave Barach9c8cfd32019-02-03 10:44:47 -0500637 fwrite(&snap->vscroll_value,
Dave Barach52642c32016-02-11 19:28:19 -0500638 sizeof(snap->vscroll_value), 1, file) != 1 ||
Dave Barach9c8cfd32019-02-03 10:44:47 -0500639 fwrite(&snap->summary_mode,
Dave Barach52642c32016-02-11 19:28:19 -0500640 sizeof(snap->summary_mode), 1, file) != 1 ||
Dave Barach9c8cfd32019-02-03 10:44:47 -0500641 fwrite(&snap->color_mode,
Dave Barach52642c32016-02-11 19:28:19 -0500642 sizeof(snap->color_mode), 1, file) != 1) {
643 error = "Error writing data";
644 }
645 records++;
646 }
647
648 if (!error) {
649 if (fclose(file)) {
650 error = "Unable to close file";
651 }
652 }
653
654 if (error) {
655 infobox(error, strerror(errno));
656 } else {
657 char buf[64];
Dave Barach9c8cfd32019-02-03 10:44:47 -0500658 snprintf(buf, sizeof(buf), "Wrote %d snapshots to snapshots.g2",
Dave Barach52642c32016-02-11 19:28:19 -0500659 records);
660 message_line(buf);
661 }
662}
663
664/****************************************************************************
665 * read_snapshot
666 *
667 * VERY primitive right now - not endian or version independent, and only reads
668 * from "snapshots.g2" in the current directory
669 ****************************************************************************/
670static void read_snapshot(void)
671{
672 FILE *file;
673 snapshot_t *snap, *next_snap;
674 snapshot_t *new_snaps = NULL;
675 char *error = NULL;
676 int len, i, records = 0;
677 pid_data_t *pp;
678
679 file = fopen("snapshots.g2", "r");
680 if (file == NULL) {
681 error = "Unable to open snapshots.g2";
682 }
683
684 /*
685 * Read in the snapshots and link them together. We insert them backwards,
686 * but that's tolerable. If the data is in anyway not what we expect, we'll
687 * probably crash. Sorry.
688 */
689 while (!error && !feof(file)) {
690 snap = g_malloc(sizeof(*snap));
691 snap->pidvec = NULL; /* so we can free this if there's an error */
692
693 len = fread(&snap->geometry, sizeof(snap->geometry), 1, file);
694 if (len == 0) {
695 /* EOF */
696 g_free(snap);
697 break;
698 } else {
699 /* insert into list straight away */
700 snap->next = new_snaps;
701 new_snaps = snap;
702 }
703 if (len != 1) {
704 error = "Problem reading first item from file";
705 break;
706 }
707 if (fread(&snap->show_event, sizeof(snap->show_event), 1, file) != 1) {
708 error = "Problem reading second item from file";
709 break;
710 }
711 len = sizeof(pid_sort_t) * g_npids;
712 snap->pidvec = g_malloc(len);
713 if (fread(snap->pidvec, len, 1, file) != 1) {
714 error = "Problem reading third item from file";
715 break;
716 }
Dave Barach9c8cfd32019-02-03 10:44:47 -0500717 if (fread(&snap->vscroll_value,
Dave Barach52642c32016-02-11 19:28:19 -0500718 sizeof(snap->vscroll_value), 1, file) != 1 ||
Dave Barach9c8cfd32019-02-03 10:44:47 -0500719 fread(&snap->summary_mode,
Dave Barach52642c32016-02-11 19:28:19 -0500720 sizeof(snap->summary_mode), 1, file) != 1 ||
Dave Barach9c8cfd32019-02-03 10:44:47 -0500721 fread(&snap->color_mode,
Dave Barach52642c32016-02-11 19:28:19 -0500722 sizeof(snap->color_mode), 1, file) != 1) {
723 error = "Problem reading final items from file";
724 break;
725 }
726
727 /*
728 * Fix up the pointers from the sorted pid vector back into our pid
729 * data objects, by walking the linked list of pid_data_t objects for
730 * every one looking for a match. This is O(n^2) grossness, but in real
731 * life there aren't that many pids, and it seems zippy enough.
732 */
733 for (i = 0; i < g_npids; i++) {
734 for (pp = g_pid_data_list; pp != NULL; pp = pp->next) {
735 if (pp->pid_value == snap->pidvec[i].pid_value) {
736 break;
737 }
738 }
739 if (pp != NULL) {
740 snap->pidvec[i].pid = pp;
741 } else {
742 error = "Snapshot file referenced unknown pids";
743 break;
744 }
745 }
746
747 records++;
748 }
749
750 if (!error) {
751 if (fclose(file)) {
752 error = "Unable to close file";
753 }
754 }
Dave Barach9c8cfd32019-02-03 10:44:47 -0500755
Dave Barach52642c32016-02-11 19:28:19 -0500756 if (error) {
757 /*
758 * Problem - clear up any detritus
759 */
760 infobox(error, strerror(errno));
761 for (snap = new_snaps; snap != NULL; snap = next_snap) {
762 next_snap = snap->next;
763 g_free(snap);
764 g_free(snap->pidvec);
765 }
766 } else {
767 /*
768 * Success! trash the old snapshots and replace with the new
769 */
770 for (snap = s_snapshots; snap != NULL; snap = next_snap) {
771 next_snap = snap->next;
772 g_free(snap->pidvec);
773 g_free(snap);
774 }
Dave Barach9c8cfd32019-02-03 10:44:47 -0500775
Dave Barach52642c32016-02-11 19:28:19 -0500776 s_cursnap = s_snapshots = new_snaps;
777 }
778
779 if (error) {
780 infobox(error, strerror(errno));
781 } else {
782 char buf[64];
Dave Barach9c8cfd32019-02-03 10:44:47 -0500783 snprintf(buf, sizeof(buf),
Dave Barach52642c32016-02-11 19:28:19 -0500784 "Read %d snapshots from snapshots.g2", records);
785 message_line(buf);
786 }
787}
788
789/****************************************************************************
790* set_color
791*
792* Set the color for the specified pid_index, or COLOR_DEFAULT to return it
793* to the usual black.
794****************************************************************************/
795#define COLOR_DEFAULT (-1)
796static void set_color(int pid_index)
797{
Dave Barach2c35e582017-04-03 10:22:17 -0400798 pid_sort_t *psp;
799
800 psp = (g_pids + pid_index);
Dave Barach9c8cfd32019-02-03 10:44:47 -0500801
Dave Barach2c35e582017-04-03 10:22:17 -0400802 if (psp->selected)
803 gdk_gc_set_foreground(da->style->black_gc, &s_color[0]);
804 else if (pid_index == COLOR_DEFAULT || !color_mode) {
Dave Barach52642c32016-02-11 19:28:19 -0500805 gdk_gc_set_foreground(da->style->black_gc, &fg_black);
806 } else {
Dave Barach9c8cfd32019-02-03 10:44:47 -0500807 gdk_gc_set_foreground(da->style->black_gc,
Dave Barach52642c32016-02-11 19:28:19 -0500808 &s_color[g_pids[pid_index].color_index]);
809 }
810}
811
812/****************************************************************************
813* toggle_event_select
814****************************************************************************/
815
Dave Barach2c35e582017-04-03 10:22:17 -0400816static int toggle_event_select(GdkEventButton *event, v1_geometry_t *vp)
Dave Barach52642c32016-02-11 19:28:19 -0500817{
818 int pid_index, start_index;
819 int x, y;
820 GdkRectangle *rp;
821 GdkRectangle hit_rect;
822 GdkRectangle dummy;
823 event_t *ep;
824 event_def_t *edp;
825 char tmpbuf [1024];
826 double time_per_pixel;
827
828 if (g_nevents == 0)
Dave Barach2c35e582017-04-03 10:22:17 -0400829 return 0;
Dave Barach52642c32016-02-11 19:28:19 -0500830
831 time_per_pixel = dtime_per_pixel(vp);
832
833 start_index = find_event_index (vp->minvistime);
834
835 /* Too far right? */
836 if (start_index >= g_nevents)
Dave Barach2c35e582017-04-03 10:22:17 -0400837 return 0;
Dave Barach9c8cfd32019-02-03 10:44:47 -0500838
839 /*
Dave Barach52642c32016-02-11 19:28:19 -0500840 * To see if the mouse hit a visible event, use a variant
841 * of the event display loop.
842 */
843
844 hit_rect.x = (int)event->x;
845 hit_rect.y = (int)event->y;
846 hit_rect.width = 1;
847 hit_rect.height = 1;
Dave Barach9c8cfd32019-02-03 10:44:47 -0500848
Dave Barach52642c32016-02-11 19:28:19 -0500849 ep = (g_events + start_index);
Dave Barach9c8cfd32019-02-03 10:44:47 -0500850
851 while ((ep->time < vp->maxvistime) &&
Dave Barach52642c32016-02-11 19:28:19 -0500852 (ep < (g_events + g_nevents))) {
853 pid_index = ep->pid->pid_index;
Dave Barach9c8cfd32019-02-03 10:44:47 -0500854
Dave Barach52642c32016-02-11 19:28:19 -0500855 /* First filter: pid out of range */
856 if ((pid_index < vp->first_pid_index) ||
857 (pid_index >= vp->first_pid_index + vp->npids)) {
858 ep++;
859 continue;
860 }
861
862 /* Second filter: event hidden */
863 edp = find_event_definition (ep->code);
864 if (!edp->selected) {
865 ep++;
866 continue;
867 }
Dave Barach9c8cfd32019-02-03 10:44:47 -0500868
869 /*
Dave Barach52642c32016-02-11 19:28:19 -0500870 * At this point, we know that the point is at least on the
Dave Barach9c8cfd32019-02-03 10:44:47 -0500871 * screen. See if the mouse hit within the bounding box
Dave Barach52642c32016-02-11 19:28:19 -0500872 */
873
Dave Barach9c8cfd32019-02-03 10:44:47 -0500874 /*
Dave Barach52642c32016-02-11 19:28:19 -0500875 * $$$$ maybe keep looping until off the edge,
876 * maintain a "best hit", then declare that one the winner?
877 */
878
879 pid_index -= vp->first_pid_index;
Dave Barach9c8cfd32019-02-03 10:44:47 -0500880
Dave Barach52642c32016-02-11 19:28:19 -0500881 y = pid_index*vp->strip_height + vp->event_offset;
Dave Barach9c8cfd32019-02-03 10:44:47 -0500882
883 x = vp->pid_ax_width +
Dave Barach52642c32016-02-11 19:28:19 -0500884 (int)(((double)(ep->time - vp->minvistime)) / time_per_pixel);
885
886 /* Perhaps we're trying to toggle the detail box? */
887 if (ep->flags & EVENT_FLAG_SELECT) {
888 /* Figure out the dimensions of the detail box */
889 format_popbox_string(tmpbuf, sizeof(tmpbuf), ep, edp);
890 rp = tbox(tmpbuf, x, y - vp->pop_offset, TBOX_GETRECT_BOXED);
891 if (gdk_rectangle_intersect(rp, &hit_rect, &dummy)) {
892 ep->flags &= ~EVENT_FLAG_SELECT;
893 view1_display_when_idle();
Dave Barach2c35e582017-04-03 10:22:17 -0400894 return 0;
Dave Barach52642c32016-02-11 19:28:19 -0500895 }
Dave Barach9c8cfd32019-02-03 10:44:47 -0500896 }
Dave Barach52642c32016-02-11 19:28:19 -0500897
Dave Barach3e07a4a2020-04-04 10:05:48 -0400898 snprintf(tmpbuf, sizeof(tmpbuf), "%ld", ep->code);
Dave Barach52642c32016-02-11 19:28:19 -0500899
900 /* Figure out the dimensions of the regular box */
901 rp = tbox(tmpbuf, x, y, TBOX_GETRECT_EVENT);
902
903 if (gdk_rectangle_intersect(rp, &hit_rect, &dummy)) {
904 /* we hit the rectangle. */
905 if (ep->flags & EVENT_FLAG_SELECT) {
906 ep->flags &= ~EVENT_FLAG_SELECT;
907 view1_display_when_idle();
Dave Barach2c35e582017-04-03 10:22:17 -0400908 return 0;
Dave Barach52642c32016-02-11 19:28:19 -0500909 } else {
910 set_color(ep->pid->pid_index);
911
912 /* It wasn't selected, so put up the detail box */
913 format_popbox_string(tmpbuf, sizeof(tmpbuf), ep, edp);
914 tbox(tmpbuf, x, y - vp->pop_offset, TBOX_DRAW_BOXED);
915 line(x, y-vp->pop_offset, x, y, LINE_DRAW_BLACK);
916 ep->flags |= EVENT_FLAG_SELECT;
917 ep->flags &= ~EVENT_FLAG_SEARCHRSLT;
918 s_last_selected_event = ep;
919 }
Dave Barach2c35e582017-04-03 10:22:17 -0400920 return 0;
Dave Barach52642c32016-02-11 19:28:19 -0500921 }
922 ep++;
923 }
Dave Barach2c35e582017-04-03 10:22:17 -0400924 return -1;
Dave Barach52642c32016-02-11 19:28:19 -0500925}
926
927/****************************************************************************
Dave Barach2c35e582017-04-03 10:22:17 -0400928* toggle_track_select
929****************************************************************************/
930
Dave Barach9c8cfd32019-02-03 10:44:47 -0500931static void toggle_track_select (GdkEventButton *event,
Dave Barach2c35e582017-04-03 10:22:17 -0400932 v1_geometry_t *vp)
933{
934 int i;
935 int pid_index;
936 int y, delta_y;
937 pid_sort_t *psp;
Dave Barach9c8cfd32019-02-03 10:44:47 -0500938
Dave Barach2c35e582017-04-03 10:22:17 -0400939 if (g_nevents == 0)
940 return;
941
942 /* Scan pid/track axis locations, looking for a match */
943 for (i = 0; i < vp->npids; i++) {
944 y = i*vp->strip_height + vp->pid_ax_offset;
945 delta_y = y - event->y;
946 if (delta_y < 0)
947 delta_y = -delta_y;
948 if (delta_y < 10) {
949 goto found;
950 }
951
952 }
953 infobox("NOTE", "\nNo PID/Track In Range\nPlease Try Again");
954 return;
Dave Barach9c8cfd32019-02-03 10:44:47 -0500955
Dave Barach2c35e582017-04-03 10:22:17 -0400956 found:
957 pid_index = i + vp->first_pid_index;
958 psp = (g_pids + pid_index);
959 psp->selected ^= 1;
960 view1_display_when_idle();
961}
962
963/****************************************************************************
964* deselect_tracks
965****************************************************************************/
966static void deselect_tracks (void)
967{
968 int i;
969
970 for (i = 0; i < g_npids; i++)
971 g_pids[i].selected = 0;
972
973}
974
975
976/****************************************************************************
Dave Barach52642c32016-02-11 19:28:19 -0500977* move_current_track
978****************************************************************************/
979
980typedef enum { MOVE_TOP, MOVE_BOTTOM } move_type;
981
Dave Barach9c8cfd32019-02-03 10:44:47 -0500982static void move_current_track(GdkEventButton *event,
Dave Barach52642c32016-02-11 19:28:19 -0500983 v1_geometry_t *vp,
984 move_type type)
985{
986 int i;
987 int pid_index;
988 int y, delta_y;
989 pid_sort_t *new_pidvec;
990 pid_sort_t *psp;
991 pid_sort_t *pold, *pnew;
992 pid_data_t *pp;
993
994 if (g_nevents == 0)
995 return;
996
997 /* Scan pid/track axis locations, looking for a match */
998 for (i = 0; i < vp->npids; i++) {
999 y = i*vp->strip_height + vp->pid_ax_offset;
1000 delta_y = y - event->y;
1001 if (delta_y < 0)
1002 delta_y = -delta_y;
1003 if (delta_y < 10) {
1004 goto found;
1005 }
1006
1007 }
1008 infobox("NOTE", "\nNo PID/Track In Range\nPlease Try Again");
1009 return;
Dave Barach9c8cfd32019-02-03 10:44:47 -05001010
Dave Barach52642c32016-02-11 19:28:19 -05001011 found:
1012 pid_index = i + vp->first_pid_index;
1013
Dave Baracha8ed6bd2017-04-04 08:00:23 -04001014 new_pidvec = g_malloc0(sizeof(pid_sort_t)*g_npids);
Dave Barach52642c32016-02-11 19:28:19 -05001015 pold = g_pids;
1016 pnew = new_pidvec;
1017
1018 if (type == MOVE_TOP) {
1019 /* move to top */
1020 *pnew++ = g_pids[pid_index];
1021 for (i = 0; i < pid_index; i++)
1022 *pnew++ = *pold++;
1023 pold++;
1024 i++;
1025 for (; i < g_npids; i++)
1026 *pnew++ = *pold++;
1027 } else {
1028 /* move to bottom */
1029 for (i = 0; i < pid_index; i++)
1030 *pnew++ = *pold++;
1031 pold++;
1032 i++;
1033 for (; i < g_npids; i++)
1034 *pnew++ = *pold++;
1035 *pnew = g_pids[pid_index];
1036 }
1037
1038 g_free(g_pids);
1039 g_pids = new_pidvec;
1040
1041 /*
Dave Barach9c8cfd32019-02-03 10:44:47 -05001042 * Revert the pid_index mapping to an identity map,
Dave Barach52642c32016-02-11 19:28:19 -05001043 */
1044 psp = g_pids;
1045
1046 for (i = 0; i < g_npids; i++) {
1047 pp = psp->pid;
1048 pp->pid_index = i;
1049 psp++;
1050 }
1051 view1_display_when_idle();
1052}
1053
1054/****************************************************************************
1055* zoom_event
Dave Barach9c8cfd32019-02-03 10:44:47 -05001056* Process a zoom gesture. The use of doubles is required to avoid
Dave Barach52642c32016-02-11 19:28:19 -05001057* truncating the various variable values, which in turn would lead to
1058* some pretty random-looking zoom responses.
1059****************************************************************************/
1060
1061void zoom_event(GdkEventButton *e1, GdkEventButton *e2, v1_geometry_t *vp)
1062{
1063 double xrange;
1064 double time_per_pixel;
1065 double width_in_pixels;
1066 double center_on_time, width_in_time;
1067 double center_on_pixel;
1068
Dave Barach9c8cfd32019-02-03 10:44:47 -05001069 /*
1070 * Clip the zoom area to the event display area.
Dave Barach52642c32016-02-11 19:28:19 -05001071 * Otherwise, center_on_time - width_in_time is in hyperspace
Dave Barach9c8cfd32019-02-03 10:44:47 -05001072 * to the left of zero
Dave Barach52642c32016-02-11 19:28:19 -05001073 */
Dave Barach9c8cfd32019-02-03 10:44:47 -05001074
Dave Barach52642c32016-02-11 19:28:19 -05001075 if (e1->x < vp->pid_ax_width)
1076 e1->x = vp->pid_ax_width;
Dave Barach9c8cfd32019-02-03 10:44:47 -05001077
Dave Barach52642c32016-02-11 19:28:19 -05001078 if (e2->x < vp->pid_ax_width)
1079 e2->x = vp->pid_ax_width;
1080
1081 if (e2->x == e1->x)
1082 goto loser_zoom_repaint;
1083
1084 xrange = (double) (e2->x - e1->x);
1085 if (xrange < 0.00)
1086 xrange = -xrange;
1087
1088 /* Actually, width in pixels of half the zoom area */
1089 width_in_pixels = xrange / 2.00;
1090 time_per_pixel = dtime_per_pixel(vp);
1091 width_in_time = width_in_pixels * time_per_pixel;
1092
1093 /* Center the screen on the center of the zoom area */
Dave Barach9c8cfd32019-02-03 10:44:47 -05001094 center_on_pixel = (double)((e2->x + e1->x) / 2.00) -
Dave Barach52642c32016-02-11 19:28:19 -05001095 (double)vp->pid_ax_width;
1096 center_on_time = center_on_pixel*time_per_pixel + (double)vp->minvistime;
1097
1098 /*
1099 * Transform back to 64-bit integer microseconds, reset the
Dave Barach9c8cfd32019-02-03 10:44:47 -05001100 * scrollbar, schedule a repaint.
Dave Barach52642c32016-02-11 19:28:19 -05001101 */
1102 vp->minvistime = (ulonglong)(center_on_time - width_in_time);
1103 vp->maxvistime = (ulonglong)(center_on_time + width_in_time);
1104
1105loser_zoom_repaint:
1106 recompute_hscrollbar();
Dave Barach9c8cfd32019-02-03 10:44:47 -05001107
Dave Barach52642c32016-02-11 19:28:19 -05001108 view1_display_when_idle();
1109}
1110
1111/****************************************************************************
1112* scroll_y
1113*
1114* Scroll up or down by the specified delta
1115*
1116****************************************************************************/
1117static void scroll_y(int delta)
1118{
1119 int new_index = s_v1->first_pid_index + delta;
1120 if (new_index + s_v1->npids > g_npids)
1121 new_index = g_npids - s_v1->npids;
1122 if (new_index < 0)
1123 new_index = 0;
Dave Barach9c8cfd32019-02-03 10:44:47 -05001124
Dave Barach52642c32016-02-11 19:28:19 -05001125 if (new_index != s_v1->first_pid_index) {
1126 s_v1->first_pid_index = new_index;
1127 GTK_ADJUSTMENT(s_view1_vsadj)->value = (gdouble)new_index;
1128 gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
1129 view1_display_when_idle();
1130 }
1131}
1132
1133/****************************************************************************
1134* view1_handle_key_press_event
1135* Relevant definitions in: /usr/include/gtk-1.2/gdk/gdktypes.h
1136*
1137* This routine implements hotkeys for the Quake generation:
1138*
1139* W - zoom in
1140* S - zoom out
1141* A - pan left
1142* D - pan right
1143* R - pan up
1144* F - pan down
1145* T - more traces
1146* G - fewer traces
1147*
1148* E - toggle summary mode
1149* C - toggle color mode
1150*
1151* X - take snapshot
1152* Z - next snapshot
1153* P - persist snapshots to file
1154* L - load snapshots from file
1155*
1156* ctrl-Q - exit
1157*
1158****************************************************************************/
1159gint
1160view1_handle_key_press_event (GtkWidget *widget, GdkEventKey *event)
1161{
1162 long long delta;
1163
1164 switch (event->keyval) {
1165 case GDK_w: // zoom in
1166 view1_button_click_callback(NULL, (gpointer)ZOOMIN_BUTTON);
1167 break;
1168
1169 case GDK_s: // zoom out
1170 view1_button_click_callback(NULL, (gpointer)ZOOMOUT_BUTTON);
1171 break;
1172
1173 case GDK_a: // pan left
1174 delta = (s_v1->maxvistime - s_v1->minvistime) / 6;
1175 if (s_v1->minvistime < delta) {
1176 delta = s_v1->minvistime;
1177 }
1178 s_v1->minvistime -= delta;
1179 s_v1->maxvistime -= delta;
1180 recompute_hscrollbar();
1181 break;
1182
1183 case GDK_d: // pan right
1184 delta = (s_v1->maxvistime - s_v1->minvistime) / 6;
1185 if (s_v1->maxvistime + delta > g_events[g_nevents - 1].time) {
1186 /*
1187 * @@@ this doesn't seem to quite reach the far right hand
1188 * side correctly - not sure why.
1189 */
1190 delta = g_events[g_nevents - 1].time - s_v1->maxvistime;
1191 }
1192 s_v1->minvistime += delta;
1193 s_v1->maxvistime += delta;
1194 recompute_hscrollbar();
1195 break;
1196
1197 case GDK_r: // pan up
1198 scroll_y(-1);
1199 break;
1200
1201 case GDK_f: // pan down
1202 scroll_y(+1);
1203 break;
1204
1205 case GDK_t: // fewer tracks
1206 view1_button_click_callback(NULL, (gpointer)LESS_TRACES_BUTTON);
1207 break;
1208
1209 case GDK_g: // more tracks
1210 view1_button_click_callback(NULL, (gpointer)MORE_TRACES_BUTTON);
1211 break;
1212
1213 case GDK_e: // toggle summary mode
1214 view1_button_click_callback
1215 (NULL, (gpointer)(unsigned long long)
1216 (summary_mode ? NOSUMMARY_BUTTON : SUMMARY_BUTTON));
1217 break;
1218
1219 case GDK_c: // toggle color mode
1220 color_mode ^= 1;
1221 view1_display_when_idle();
1222 break;
1223
1224 case GDK_p: // persist snapshots
1225 write_snapshot();
1226 break;
1227
1228 case GDK_l: // load snapshots
1229 read_snapshot();
1230 break;
1231
1232 case GDK_x: // take snapshot
1233 view1_button_click_callback(NULL, (gpointer)SNAP_BUTTON);
1234 break;
1235
1236 case GDK_z: // next snapshot
1237 view1_button_click_callback(NULL, (gpointer)NEXT_BUTTON);
1238 break;
1239
1240 case GDK_q: // ctrl-q is exit
1241 if (event->state & GDK_CONTROL_MASK) {
1242 gtk_main_quit();
1243 }
1244 break;
1245 }
1246 return TRUE;
1247}
1248
1249/****************************************************************************
1250* button_press_event
1251* Relevant definitions in: /usr/include/gtk-1.2/gdk/gdktypes.h
1252*
1253* This routine implements three functions: zoom-to-area, time ruler, and
Dave Barach9c8cfd32019-02-03 10:44:47 -05001254* show/hide event detail popup.
Dave Barach52642c32016-02-11 19:28:19 -05001255*
Dave Barach9c8cfd32019-02-03 10:44:47 -05001256* The left mouse button (button 1) has two simultaneous functions: event
Dave Barach52642c32016-02-11 19:28:19 -05001257* detail popup, and zoom-to-area. If the press and release events occur
1258* within a small delta-x, it's a detail popup event. Otherwise, it's
1259* an area zoom.
1260*
1261* The right mouse button (button 3) implements the time ruler.
1262****************************************************************************/
1263
1264static gint
1265button_press_event (GtkWidget *widget, GdkEventButton *event)
1266{
1267 static GdkEventButton press1_event;
1268 static boolean press1_valid;
1269 static GdkEventButton press3_event;
1270 static guint32 last_truler_time;
1271 static boolean press3_valid;
1272 static boolean zoom_bar_up;
1273 int time_ax_y, xdelta;
1274 char tmpbuf [128];
1275 double time_per_pixel;
1276
1277 time_ax_y = 0;
1278
1279 switch(event->type) {
1280 case GDK_BUTTON_PRESS:
1281 /* Capture the appropriate starting point */
1282 if (event->button == 1) {
1283 press1_valid = TRUE;
1284 press1_event = *event;
1285 return(TRUE);
1286 }
1287 if (event->button == 3) {
1288 press3_valid = TRUE;
1289 press3_event = *event;
1290 return(TRUE);
1291 }
1292 return(TRUE);
1293
1294 case GDK_BUTTON_RELEASE:
1295 /* Time ruler */
1296 if (press3_valid) {
1297 press3_valid = FALSE;
1298 /* Fix the cursor, and repaint the screen from scratch */
1299 gdk_window_set_cursor (da->window, norm_cursor);
1300 view1_display_when_idle();
1301 return(TRUE);
1302 }
1303 /* Event select / zoom-to-area */
1304 if (press1_valid) {
1305 press1_valid = FALSE;
1306 xdelta = (int)(press1_event.x - event->x);
1307 if (xdelta < 0)
1308 xdelta = -xdelta;
1309
1310 /* is the mouse more or less where it started? */
1311 if (xdelta < 10) {
1312 /* Control-left-mouse => sink the track */
1313 /* Shift-left-mouse => raise the track */
1314 if ((press1_event.state & GDK_CONTROL_MASK) ==
1315 GDK_CONTROL_MASK) {
1316 move_current_track(event, s_v1, MOVE_BOTTOM);
1317 } else if ((press1_event.state & GDK_SHIFT_MASK) ==
1318 GDK_SHIFT_MASK) {
1319 move_current_track(event, s_v1, MOVE_TOP);
1320 } else {
Dave Barach2c35e582017-04-03 10:22:17 -04001321 /* No modifiers: toggle the event / select track */
1322 if (toggle_event_select(event, s_v1))
1323 toggle_track_select(event, s_v1);
Dave Barach52642c32016-02-11 19:28:19 -05001324 }
1325 /* Repaint to get rid of the zoom bar */
1326 if (zoom_bar_up) {
1327 /* Fix the cursor and leave. No zoom */
1328 gdk_window_set_cursor (da->window, norm_cursor);
1329 zoom_bar_up = FALSE;
1330 break;
1331 }
1332 } else { /* mouse moved enough to zoom */
1333 zoom_event(&press1_event, event, s_v1);
1334 gdk_window_set_cursor (da->window, norm_cursor);
1335 zoom_bar_up = FALSE;
1336 }
1337 } else if (event->button == 4) {
1338 /* scroll wheel up */
1339 scroll_y(event->state & GDK_SHIFT_MASK ? -10 : -1);
1340 } else if (event->button == 5) {
1341 /* scroll wheel down */
1342 scroll_y(event->state & GDK_SHIFT_MASK ? +10 : +1);
1343 }
1344 return(TRUE);
1345
1346 case GDK_MOTION_NOTIFY:
1347 /* Button one followed by motion: draw zoom fence and fix cursor */
1348 if (press1_valid) {
1349 /* Fence, cursor already set */
1350 if (zoom_bar_up)
1351 return(TRUE);
Dave Barach9c8cfd32019-02-03 10:44:47 -05001352
Dave Barach52642c32016-02-11 19:28:19 -05001353 xdelta = (int)(press1_event.x - event->x);
1354 if (xdelta < 0)
1355 xdelta = -xdelta;
Dave Barach9c8cfd32019-02-03 10:44:47 -05001356
Dave Barach52642c32016-02-11 19:28:19 -05001357 /* Haven't moved enough to declare a zoom sequence yet */
Dave Barach9c8cfd32019-02-03 10:44:47 -05001358 if (xdelta < 10)
Dave Barach52642c32016-02-11 19:28:19 -05001359 return(TRUE);
Dave Barach9c8cfd32019-02-03 10:44:47 -05001360
Dave Barach52642c32016-02-11 19:28:19 -05001361 /* Draw the zoom fence, use the key-down X coordinate */
1362 time_ax_y = s_v1->npids * s_v1->strip_height + s_v1->pid_ax_offset;
Dave Barach9c8cfd32019-02-03 10:44:47 -05001363
1364 line((int)(press1_event.x), s_v1->pop_offset,
Dave Barach52642c32016-02-11 19:28:19 -05001365 (int)(press1_event.x), time_ax_y, LINE_DRAW_BLACK);
1366 tbox("Zoom From Here...", (int)(press1_event.x), s_v1->pop_offset,
1367 TBOX_DRAW_BOXED);
1368 gdk_window_set_cursor(da->window, zi_cursor);
1369 zoom_bar_up = TRUE;
1370 return(TRUE);
1371 }
1372 if (press3_valid) {
1373 double nsec;
1374
1375 gdk_window_set_cursor(da->window, zi_cursor);
1376
Dave Barach9c8cfd32019-02-03 10:44:47 -05001377 /*
Dave Barach52642c32016-02-11 19:28:19 -05001378 * Some filtration is needed on Solaris, or the server will hang
1379 */
1380 if (event->time - last_truler_time < 75)
1381 return(TRUE);
1382
1383 last_truler_time = event->time;
1384
Dave Barach9c8cfd32019-02-03 10:44:47 -05001385 line((int)(press3_event.x), s_v1->pop_offset,
Dave Barach52642c32016-02-11 19:28:19 -05001386 (int)(press3_event.x), time_ax_y, LINE_DRAW_BLACK);
1387
1388 xdelta = (int)(press3_event.x - event->x);
1389 if (xdelta < 0)
1390 xdelta = -xdelta;
Dave Barach9c8cfd32019-02-03 10:44:47 -05001391
1392 time_per_pixel = ((double)(s_v1->maxvistime - s_v1->minvistime)) /
1393 ((double)(s_v1->total_width - s_v1->pid_ax_width));
Dave Barach52642c32016-02-11 19:28:19 -05001394
1395 time_ax_y = s_v1->npids * s_v1->strip_height + s_v1->pid_ax_offset;
1396
Dave Barach9c8cfd32019-02-03 10:44:47 -05001397 line((int)(press3_event.x), s_v1->pop_offset,
Dave Barach52642c32016-02-11 19:28:19 -05001398 (int)(press3_event.x), time_ax_y, LINE_DRAW_BLACK);
1399 /*
1400 * Note: use a fixed-width format so it looks like we're
Dave Barach9c8cfd32019-02-03 10:44:47 -05001401 * erasing and redrawing the box.
Dave Barach52642c32016-02-11 19:28:19 -05001402 */
1403 nsec = ((double)xdelta)*time_per_pixel;
1404 if (nsec >1e9) {
Dave Barach3e07a4a2020-04-04 10:05:48 -04001405 snprintf(tmpbuf, sizeof(tmpbuf), "%8.3f sec ", nsec/1e9);
Dave Barach52642c32016-02-11 19:28:19 -05001406 } else if (nsec > 1e6) {
Dave Barach3e07a4a2020-04-04 10:05:48 -04001407 snprintf(tmpbuf, sizeof(tmpbuf), "%8.3f msec", nsec/1e6);
Dave Barach52642c32016-02-11 19:28:19 -05001408 } else if (nsec > 1e3) {
Dave Barach3e07a4a2020-04-04 10:05:48 -04001409 snprintf(tmpbuf, sizeof(tmpbuf), "%8.3f usec", nsec/1e3);
Dave Barach52642c32016-02-11 19:28:19 -05001410 } else {
Dave Barach3e07a4a2020-04-04 10:05:48 -04001411 snprintf(tmpbuf, sizeof(tmpbuf), "%8.0f nsec", nsec);
Dave Barach52642c32016-02-11 19:28:19 -05001412 }
Dave Barach2c35e582017-04-03 10:22:17 -04001413 s_v1->last_time_interval = nsec;
Dave Barach52642c32016-02-11 19:28:19 -05001414 tbox(tmpbuf, (int)(press3_event.x), s_v1->pop_offset,
1415 TBOX_DRAW_BOXED);
1416 return(TRUE);
1417 }
1418
1419 default:
1420 break;
1421#ifdef DEBUG
1422 g_print("button:\ttype = %d\n", event->type);
1423 g_print("\twindow = 0x%x\n", event->window);
1424 g_print("\tsend_event = %d\n", event->send_event);
1425 g_print("\ttime = %d\n", event->time);
1426 g_print("\tx = %6.2f\n", event->x);
1427 g_print("\ty = %6.2f\n", event->y);
1428 g_print("\tpressure = %6.2f\n", event->pressure);
1429 g_print("\txtilt = %6.2f\n", event->xtilt);
1430 g_print("\tytilt = %6.2f\n", event->ytilt);
1431 g_print("\tstate = %d\n", event->state);
1432 g_print("\tbutton = %d\n", event->button);
1433 g_print("\tsource = %d\n", event->source);
1434 g_print("\tdeviceid = %d\n", event->deviceid);
1435 g_print("\tx_root = %6.2f\n", event->x_root);
1436 g_print("\ty_root = %6.2f\n", event->y_root);
1437 return(TRUE);
1438#endif
1439 }
1440
1441 view1_display_when_idle();
1442
1443 return(TRUE);
1444}
1445
1446/****************************************************************************
1447* configure_event
1448* Happens when the window manager resizes the viewer's main window.
1449****************************************************************************/
1450
1451static gint
1452configure_event (GtkWidget *widget, GdkEventConfigure *event)
1453{
1454 /* Toss the previous drawing area backing store pixmap */
1455 if (pm)
1456 gdk_pixmap_unref(pm);
Dave Barach9c8cfd32019-02-03 10:44:47 -05001457
Dave Barach52642c32016-02-11 19:28:19 -05001458 /* Create a new pixmap, paint it */
1459 pm = gdk_pixmap_new(widget->window,
1460 widget->allocation.width,
1461 widget->allocation.height,
1462 -1);
1463 gdk_draw_rectangle (pm,
1464 widget->style->white_gc,
1465 TRUE,
1466 0, 0,
1467 widget->allocation.width,
1468 widget->allocation.height);
1469
1470 /* Reset the view geometry parameters, as required */
1471 s_v1->total_width = widget->allocation.width;
1472 s_v1->total_height = widget->allocation.height;
Dave Barach9c8cfd32019-02-03 10:44:47 -05001473 s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) /
Dave Barach52642c32016-02-11 19:28:19 -05001474 s_v1->strip_height;
1475
1476 /* Schedule a repaint */
1477 view1_display_when_idle();
1478 return(TRUE);
1479}
1480
1481/****************************************************************************
1482* expose_event
1483* Use backing store to fix the screen.
1484****************************************************************************/
1485static gint expose_event (GtkWidget *widget, GdkEventExpose *event)
1486{
1487 gdk_draw_pixmap(widget->window,
1488 widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
1489 pm,
1490 event->area.x, event->area.y,
1491 event->area.x, event->area.y,
1492 event->area.width, event->area.height);
Dave Barach9c8cfd32019-02-03 10:44:47 -05001493
Dave Barach52642c32016-02-11 19:28:19 -05001494 return(FALSE);
1495}
1496
1497/****************************************************************************
1498* event_search_internal
1499* This routine searches forward from s_srchindex, looking for s_srchcode;
1500* wraps at the end of the buffer.
1501****************************************************************************/
1502
1503boolean event_search_internal (void)
1504{
1505 event_t *ep;
1506 int i;
1507 int index;
1508 int pid_index;
1509 boolean full_redisplay = FALSE;
1510 ulonglong current_width;
1511 char tmpbuf [64];
1512
1513 /* No events yet? Act like the search worked, to avoid a loop */
1514 if (g_nevents == 0)
1515 return(TRUE);
1516
1517 ep = (g_events + s_srchindex);
1518 ep->flags &= ~EVENT_FLAG_SEARCHRSLT;
1519
Dave Barach9c8cfd32019-02-03 10:44:47 -05001520 /*
Dave Barach52642c32016-02-11 19:28:19 -05001521 * Assume the user wants to search [plus or minus]
1522 * from where they are.
1523 */
1524#ifdef notdef
1525 if (ep->time < s_v1->minvistime)
1526 s_srchindex = find_event_index (s_v1->minvistime);
1527#endif
1528
1529 for (i = 1; i <= g_nevents; i++) {
1530 index = (srch_chase_dir == SRCH_CHASE_BACKWARD) ?
1531 (s_srchindex - i) % g_nevents :
1532 (i + s_srchindex) % g_nevents;
Dave Barach9c8cfd32019-02-03 10:44:47 -05001533
Dave Barach52642c32016-02-11 19:28:19 -05001534 ep = (g_events + index);
Dave Barach9c8cfd32019-02-03 10:44:47 -05001535
Dave Barach52642c32016-02-11 19:28:19 -05001536 if (ep->code == s_srchcode) {
1537 if (s_srchfail_up)
1538 message_line("");
1539 s_srchindex = index;
1540 pid_index = ep->pid->pid_index;
Dave Barach9c8cfd32019-02-03 10:44:47 -05001541
Dave Barach52642c32016-02-11 19:28:19 -05001542 /* Need a vertical scroll? */
1543 if ((pid_index < s_v1->first_pid_index) ||
1544 (pid_index >= s_v1->first_pid_index + s_v1->npids)) {
1545 if (pid_index > (g_npids - s_v1->npids))
1546 pid_index = (g_npids - s_v1->npids);
1547 s_v1->first_pid_index = pid_index;
Dave Barach9c8cfd32019-02-03 10:44:47 -05001548 GTK_ADJUSTMENT(s_view1_vsadj)->value =
Dave Barach52642c32016-02-11 19:28:19 -05001549 (gdouble)s_v1->first_pid_index;
1550 gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
1551 full_redisplay = TRUE;
1552 }
Dave Barach9c8cfd32019-02-03 10:44:47 -05001553
Dave Barach52642c32016-02-11 19:28:19 -05001554 /* Need a horizontal scroll? */
1555 if (ep->time < s_v1->minvistime || ep->time > s_v1->maxvistime) {
1556 current_width = (s_v1->maxvistime - s_v1->minvistime);
1557 if (ep->time < ((current_width+1) / 2)) {
1558 s_v1->minvistime = 0ll;
1559 s_v1->maxvistime = current_width;
1560 } else {
1561 s_v1->minvistime = ep->time - ((current_width+1)/2);
1562 s_v1->maxvistime = ep->time + ((current_width+1)/2);
1563 }
1564 recompute_hscrollbar();
1565 full_redisplay = TRUE;
1566 }
1567 ep->flags |= EVENT_FLAG_SEARCHRSLT;
1568 full_redisplay = TRUE;
1569
1570#ifdef NOTDEF
1571 if (!full_redisplay){
1572 if (!s_result_up) {
1573 s_result_up = TRUE;
1574 time_per_pixel = dtime_per_pixel(s_v1);
Dave Barach9c8cfd32019-02-03 10:44:47 -05001575
Dave Barach52642c32016-02-11 19:28:19 -05001576 y = pid_index*s_v1->strip_height + s_v1->event_offset;
Dave Barach9c8cfd32019-02-03 10:44:47 -05001577 x = s_v1->pid_ax_width +
1578 (int)(((double)(ep->time - s_v1->minvistime)) /
Dave Barach52642c32016-02-11 19:28:19 -05001579 time_per_pixel);
Dave Barach3e07a4a2020-04-04 10:05:48 -04001580 snprintf(tmpbuf, sizeof(tmpbuf), "SEARCH RESULT");
Dave Barach52642c32016-02-11 19:28:19 -05001581 tbox(tmpbuf, x, y - s_v1->pop_offset, TBOX_DRAW_BOXED);
1582 line(x, y-s_v1->pop_offset, x, y, LINE_DRAW_BLACK);
1583 } else {
1584 full_redisplay = TRUE;
1585 }
1586 }
1587#endif
1588
1589 if (full_redisplay)
1590 view1_display_when_idle();
1591 return(TRUE);
1592 }
1593 }
Dave Barach3e07a4a2020-04-04 10:05:48 -04001594 snprintf (tmpbuf, sizeof(tmpbuf),
1595 "Search for event %ld failed...\n", s_srchcode);
Dave Barach52642c32016-02-11 19:28:19 -05001596 message_line(tmpbuf);
1597 s_srchfail_up = TRUE;
1598 return(TRUE);
1599}
1600
1601/****************************************************************************
1602* event_search_callback
1603****************************************************************************/
1604
1605boolean event_search_callback (char *s)
1606{
1607 /* No events yet? Act like the search worked, to avoid a loop */
1608 if (g_nevents == 0)
1609 return(TRUE);
1610
1611 s_srchcode = atol(s);
Dave Barach9c8cfd32019-02-03 10:44:47 -05001612
Dave Barach52642c32016-02-11 19:28:19 -05001613 if (s_srchcode == 0)
1614 return(FALSE);
1615
1616 return(event_search_internal());
1617}
1618
Dave Barach9c8cfd32019-02-03 10:44:47 -05001619
1620/****************************************************************************
1621* anomaly_statistics_init
1622****************************************************************************/
1623
1624static int anomaly_statistics_init (void)
1625{
1626 elog_event_t *eep;
1627 u32 data;
1628 event_t *ep;
1629 pid_data_t *pid;
1630 int i;
Dave Barach9c8cfd32019-02-03 10:44:47 -05001631 f64 fdata;
1632
1633 /* Gather summary statistics... */
1634 ep = g_events;
1635
1636 vec_reset_length (s_v1->means);
1637 vec_reset_length (s_v1->matches);
1638 vec_reset_length (s_v1->variances);
1639 vec_reset_length (s_v1->two_stddevs);
Dave Barach3117ad82019-02-04 17:41:29 -05001640 vec_reset_length (s_v1->mins);
1641 vec_reset_length (s_v1->maxes);
Dave Barach9c8cfd32019-02-03 10:44:47 -05001642
1643 for (i = 0; i < g_nevents; i++) {
1644 if (ep->code != s_anomalycode) {
1645 ep++;
1646 continue;
1647 }
1648 pid = ep->pid;
Dave Barach9c8cfd32019-02-03 10:44:47 -05001649 vec_validate_init_empty (s_v1->matches, pid->pid_index, 0);
Dave Barach3117ad82019-02-04 17:41:29 -05001650 vec_validate_init_empty (s_v1->means, pid->pid_index, 0.0);
1651 vec_validate_init_empty (s_v1->mins, pid->pid_index, 0.0);
1652 vec_validate_init_empty (s_v1->maxes, pid->pid_index, 0.0);
Dave Barach9c8cfd32019-02-03 10:44:47 -05001653 eep = get_clib_event (ep->datum);
1654 data = clib_mem_unaligned (eep->data, u32);
1655 fdata = data;
1656 s_v1->means[pid->pid_index] += fdata;
1657 s_v1->matches[pid->pid_index] += 1;
Dave Barach3117ad82019-02-04 17:41:29 -05001658 /* First data point? set min, max */
1659 if (PREDICT_FALSE(s_v1->matches[pid->pid_index] == 1)) {
1660 s_v1->mins[pid->pid_index] = fdata;
1661 s_v1->maxes[pid->pid_index] = fdata;
1662 } else {
1663 s_v1->mins[pid->pid_index] = (fdata < s_v1->mins[pid->pid_index]) ?
1664 fdata : s_v1->mins[pid->pid_index];
1665 s_v1->maxes[pid->pid_index] =
1666 (fdata > s_v1->maxes[pid->pid_index]) ?
1667 fdata : s_v1->maxes[pid->pid_index];
1668 }
Dave Barach9c8cfd32019-02-03 10:44:47 -05001669 ep++;
1670 }
1671 if (vec_len (s_v1->matches) == 0)
1672 return -1;
1673
1674 /* Compute s_v1->means */
1675 for (i = 0; i < vec_len (s_v1->means); i++)
1676 s_v1->means[i] = s_v1->matches[i]
1677 ? (s_v1->means[i] / (f64) s_v1->matches[i]) : 0.0;
1678
1679 /* Compute s_v1->variances */
1680 ep = g_events;
1681 for (i = 0; i < g_nevents; i++) {
1682 if (ep->code != s_anomalycode) {
1683 ep++;
1684 continue;
1685 }
1686 pid = ep->pid;
1687 vec_validate_init_empty (s_v1->variances, pid->pid_index, 0);
1688 eep = get_clib_event (ep->datum);
1689 data = clib_mem_unaligned (eep->data, u32);
1690 fdata = data;
1691 s_v1->variances[pid->pid_index] +=
1692 (fdata - s_v1->means[pid->pid_index])
1693 * (fdata - s_v1->means[pid->pid_index]);
1694 ep++;
1695 }
1696
1697 /* Normalize variances */
1698 for (i = 0; i < vec_len (s_v1->variances); i++)
1699 s_v1->variances[i] = s_v1->matches[i]
1700 ? (s_v1->variances[i] / (f64) s_v1->matches[i]) : 0.0;
1701
1702 /* Compute the anomaly threshold, by default 2.5*stddev */
1703 for (i = 0; i < vec_len (s_v1->variances); i++)
1704 vec_add1 (s_v1->two_stddevs,
1705 s_v1->anomaly_threshold_stddevs * sqrt(s_v1->variances[i]));
1706 return 0;
1707}
1708
1709/****************************************************************************
1710* anomaly_search_internal
1711* This routine searches forward from s_srchindex, looking for s_srchcode;
1712* wraps at the end of the buffer.
1713****************************************************************************/
1714
1715boolean anomaly_search_internal (void)
1716{
1717 elog_event_t *eep;
1718 u32 data;
1719 event_t *ep;
1720 pid_data_t *pid;
1721 int i;
1722 int index;
1723 int pid_index;
1724 boolean full_redisplay = FALSE;
1725 ulonglong current_width;
1726 char tmpbuf [64];
1727 f64 fdata;
1728
1729 if (vec_len (s_v1->matches) == 0)
1730 anomaly_statistics_init();
1731
1732 ep = (g_events + s_srchindex);
1733 ep->flags &= ~EVENT_FLAG_SEARCHRSLT;
1734
1735 /*
1736 * If the user rearranged the screen, start from the minimum
1737 * visible time
1738 */
1739 if (ep->time < s_v1->minvistime)
1740 s_srchindex = find_event_index (s_v1->minvistime);
1741
1742 for (i = 1; i <= g_nevents; i++) {
1743 index = (i + s_srchindex) % g_nevents;
1744
1745 ep = (g_events + index);
1746 if (ep->code != s_anomalycode)
1747 continue;
1748 pid = ep->pid;
1749
1750 eep = get_clib_event (ep->datum);
1751 data = clib_mem_unaligned (eep->data, u32);
1752 fdata = data;
1753
1754 /*
1755 * Found an anomaly? Define an anomaly as a datum
1756 * greater than 2*stddev above average.
1757 */
1758 if ((fdata - s_v1->means[pid->pid_index]) >
1759 s_v1->two_stddevs[pid->pid_index]) {
1760 u8 *s;
1761
Dave Barach3117ad82019-02-04 17:41:29 -05001762 s = format (0, "%.1f*stddev {min,max,mean,threshold}: ",
Dave Barach9c8cfd32019-02-03 10:44:47 -05001763 s_v1->anomaly_threshold_stddevs);
1764
1765 for (i = 0; i < vec_len (s_v1->means); i++) {
Dave Barach3117ad82019-02-04 17:41:29 -05001766 if (s_v1->matches[i] > 0)
1767 s = format (s, "{%.0f, %.0f, %.0f, %.0f} ",
1768 s_v1->mins[i], s_v1->maxes[i],
1769 s_v1->means[i],
1770 s_v1->means[i]+s_v1->two_stddevs[i]);
1771 else
1772 s = format (s, "{no match} ");
Dave Barach9c8cfd32019-02-03 10:44:47 -05001773 }
1774
1775 message_line ((char *)s);
1776 vec_free (s);
1777
1778 s_srchindex = index;
1779 pid_index = ep->pid->pid_index;
1780
1781 /* Need a vertical scroll? */
1782 if ((pid_index < s_v1->first_pid_index) ||
1783 (pid_index >= s_v1->first_pid_index + s_v1->npids)) {
1784 if (pid_index > (g_npids - s_v1->npids))
1785 pid_index = (g_npids - s_v1->npids);
1786 s_v1->first_pid_index = pid_index;
1787 GTK_ADJUSTMENT(s_view1_vsadj)->value =
1788 (gdouble)s_v1->first_pid_index;
1789 gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
1790 full_redisplay = TRUE;
1791 }
1792
1793 /* Need a horizontal scroll? */
1794 if (ep->time < s_v1->minvistime || ep->time > s_v1->maxvistime) {
1795 current_width = (s_v1->maxvistime - s_v1->minvistime);
1796 if (ep->time < ((current_width+1) / 2)) {
1797 s_v1->minvistime = 0ll;
1798 s_v1->maxvistime = current_width;
1799 } else {
1800 s_v1->minvistime = ep->time - ((current_width+1)/2);
1801 s_v1->maxvistime = ep->time + ((current_width+1)/2);
1802 }
1803 recompute_hscrollbar();
1804 full_redisplay = TRUE;
1805 }
1806 ep->flags |= EVENT_FLAG_SEARCHRSLT;
1807 full_redisplay = TRUE;
1808
1809 if (full_redisplay)
1810 view1_display_when_idle();
1811
1812 return(TRUE);
1813 }
1814 }
Dave Barach3e07a4a2020-04-04 10:05:48 -04001815 snprintf (tmpbuf, sizeof(tmpbuf),
1816 "Search for an anomalous event %ld failed...\n",
1817 s_anomalycode);
Dave Barach9c8cfd32019-02-03 10:44:47 -05001818 message_line(tmpbuf);
1819 s_srchfail_up = TRUE;
1820 return(TRUE);
1821}
1822
1823/****************************************************************************
1824* anomaly_search_callback
1825****************************************************************************/
1826
1827boolean anomaly_search_callback (char *s)
1828{
1829 ulong new_anomalycode;
1830
1831 /* No events yet? Act like the search worked, to avoid a loop */
1832 if (g_nevents == 0)
1833 return(TRUE);
1834
1835 new_anomalycode = atol(s);
1836
1837 if (new_anomalycode == 0)
1838 return(FALSE);
1839
1840 if (new_anomalycode != s_anomalycode ||
1841 vec_len (s_v1->matches) == 0) {
1842 s_anomalycode = new_anomalycode;
1843 if (anomaly_statistics_init()) {
1844 u8 *s;
1845
1846 s = format (0, "Search for an anomalous event %ld failed...\n",
1847 s_anomalycode);
1848 message_line ((char *) s);
1849 vec_free (s);
1850 return (TRUE);
1851 }
1852 }
1853 return(anomaly_search_internal());
1854}
1855
1856/****************************************************************************
1857* anomaly_threshold_callback
1858****************************************************************************/
1859
1860boolean anomaly_threshold_callback (char *s)
1861{
1862 f64 new_threshold;
1863
1864 /* No events yet? Act like the search worked, to avoid a loop */
1865 if (g_nevents == 0)
1866 return(TRUE);
1867
1868 new_threshold = atof (s);
1869
1870 if (new_threshold == 0.0 || new_threshold > 10.0)
1871 return(FALSE);
1872
1873 s_v1->anomaly_threshold_stddevs = new_threshold;
1874
1875 vec_reset_length (s_v1->means);
1876 vec_reset_length (s_v1->matches);
1877 vec_reset_length (s_v1->variances);
1878 vec_reset_length (s_v1->two_stddevs);
1879 return (TRUE);
1880}
1881
Dave Barach52642c32016-02-11 19:28:19 -05001882/****************************************************************************
1883* event_search
1884****************************************************************************/
1885
1886static void event_search (void)
1887{
1888 modal_dialog ("Event Search: Please Enter Event Code",
1889 "Invalid: Please Reenter Event Code", NULL,
1890 event_search_callback);
1891}
1892
1893/****************************************************************************
Dave Barach9c8cfd32019-02-03 10:44:47 -05001894* anomaly_search
1895****************************************************************************/
1896
1897static void anomaly_search (void)
1898{
1899 modal_dialog ("Anomaly Search: Please Enter Event Code",
1900 "Invalid: Please Reenter Event Code", NULL,
1901 anomaly_search_callback);
1902}
1903
1904/****************************************************************************
1905* anomaly_threshold
1906****************************************************************************/
1907
1908static void anomaly_threshold (void)
1909{
1910 modal_dialog ("Anomaly Threshold: Please Enter Threshold",
1911 "Invalid: Please Reenter Threshold in Standard Deviations",
1912 NULL, anomaly_threshold_callback);
1913}
1914
1915/****************************************************************************
Dave Barach52642c32016-02-11 19:28:19 -05001916* init_track_colors
1917****************************************************************************/
1918static void init_track_colors(void)
1919{
1920 int i;
1921 unsigned hash;
1922 char *label_char;
1923 unsigned RGB[3];
1924 gboolean dont_care[g_npids];
1925
1926 /*
1927 * If we've already allocated the colors once, then in theory we should
1928 * just be able to re-order the GCs already created to match the new track
1929 * order; the track -> color mapping doesn't currently change at runtime.
1930 * However, it's easier just to allocate everything from fresh. As a nod in
1931 * the direction of politeness towards our poor abused X server, we at
1932 * least mop up the previously allocated GCs first, although in practice
Dave Barach9c8cfd32019-02-03 10:44:47 -05001933 * even omitting this didn't seem to cause a problem.
Dave Barach52642c32016-02-11 19:28:19 -05001934 */
1935 if (s_color != NULL ) {
Dave Barach9c8cfd32019-02-03 10:44:47 -05001936 gdk_colormap_free_colors(gtk_widget_get_colormap(da),
Dave Barach52642c32016-02-11 19:28:19 -05001937 s_color, g_npids);
Dave Barachb7b92992018-10-17 10:38:51 -04001938 clib_memset(s_color, 0, sizeof(GdkColor) * g_npids);
Dave Barach52642c32016-02-11 19:28:19 -05001939 } else {
1940 /*
1941 * First time through: allocate the array to hold the GCs.
1942 */
Dave Barach2c35e582017-04-03 10:22:17 -04001943 s_color = g_malloc(sizeof(GdkColor) * (g_npids+1));
Dave Barach52642c32016-02-11 19:28:19 -05001944 }
1945
1946 /*
1947 * Go through and assign a color for each track.
1948 */
Dave Barach2c35e582017-04-03 10:22:17 -04001949 /* Setup entry 0 in the colormap as pure red (for selection) */
1950 s_color[0] = fg_red;
1951
1952 for (i = 1; i < g_npids; i++) {
Dave Barach52642c32016-02-11 19:28:19 -05001953 /*
1954 * We compute the color from a hash of the thread name. That way we get
1955 * a distribution of different colors, and the same thread has the same
1956 * color across multiple data sets. Unfortunately, even though the
1957 * process name and thread id are invariant across data sets, the
1958 * process id isn't, so we want to exclude that from the hash. Since
1959 * the pid appears in parentheses after the process name and tid, we
1960 * can just stop at the '(' character.
1961 *
1962 * We could create a substring and use the CLIB Jenkins hash, but given
1963 * we're hashing ascii data, a suitable Bernstein hash is pretty much
1964 * just as good, and it's easiest just to compute it inline.
1965 */
1966 label_char = get_track_label(g_pids[i].pid_value);
1967 hash = 0;
1968 while (*label_char != '\0' && *label_char != '(') {
1969 hash = hash * 33 + *label_char++;
1970 }
1971 hash += hash >> 5; /* even out the lower order bits a touch */
1972
1973 /*
1974 * OK, now we have our hash. We get the color by using the first three
1975 * bytes of the hash for the RGB values (expanded from 8 to 16 bits),
1976 * and then use the fourth byte to choose one of R, G, B and mask this
1977 * one down. This ensures the color can't be too close to white and
1978 * therefore hard to see.
1979 *
1980 * We also drop the top bit of the green, since bright green on its own
1981 * is hard to see against white. Generally we err on the side of
1982 * keeping it dark, rather than using the full spectrum of colors. This
1983 * does result in something of a preponderance of muddy colors and a
1984 * bit of a lack of cheery bright ones, but at least you can read
1985 * everything. It would be nice to do better.
1986 */
1987 RGB[0] = (hash & 0xff000000) >> 16;
1988 RGB[1] = (hash & 0x007f0000) >> 8;
1989 RGB[2] = (hash & 0x0000ff00);
1990 RGB[hash % 3] &= 0x1fff;
1991
1992 {
1993 GdkColor color = {0, RGB[0], RGB[1], RGB[2]};
1994 s_color[i] = color;
1995 g_pids[i].color_index = i;
1996 }
1997 }
1998
1999 /*
2000 * Actually allocate the colors in one bulk operation. We ignore the return
2001 * values.
2002 */
Dave Barach9c8cfd32019-02-03 10:44:47 -05002003 gdk_colormap_alloc_colors(gtk_widget_get_colormap(da),
Dave Barach2c35e582017-04-03 10:22:17 -04002004 s_color, g_npids+1, FALSE, TRUE, dont_care);
Dave Barach52642c32016-02-11 19:28:19 -05002005}
2006
2007
2008/****************************************************************************
2009* chase_event_etc
2010* Reorder the pid_index fields so the viewer "chases" the last selected
2011* event.
2012****************************************************************************/
2013
2014static void chase_event_etc(enum chase_mode mode)
2015{
2016 pid_sort_t *psp, *new_pidvec;
2017 pid_data_t *pp;
2018 event_t *ep;
2019 int pids_mapped;
2020 ulong code_to_chase;
2021 ulong datum_to_chase;
2022 ulong pid_to_chase;
2023 int i;
2024 int winner;
2025
2026 if (!s_last_selected_event) {
Dave Barach9c8cfd32019-02-03 10:44:47 -05002027 infobox("No selected event",
Dave Barach52642c32016-02-11 19:28:19 -05002028 "\nPlease select an event and try again...\n");
2029 return;
2030 }
2031
2032 /* Clear all index assignments */
2033 psp = g_pids;
2034 for (i = 0; i < g_npids; i++) {
2035 pp = psp->pid;
2036 pp->pid_index = 0xFFFFFFFF;
2037 psp++;
2038 }
2039
2040 ep = s_last_selected_event;
2041 code_to_chase = ep->code;
2042 datum_to_chase = ep->datum;
2043 pid_to_chase = ep->pid->pid_value;
2044 pids_mapped = 0;
Dave Baracha8ed6bd2017-04-04 08:00:23 -04002045 new_pidvec = g_malloc0(sizeof(pid_sort_t)*g_npids);
Dave Barach52642c32016-02-11 19:28:19 -05002046
2047 while (1) {
2048 if (srch_chase_dir == SRCH_CHASE_FORWARD) {
2049 if (ep >= g_events + g_nevents)
2050 break;
2051 } else {
2052 if (ep < g_events)
2053 break;
2054 }
2055
2056 winner = 0;
2057 switch(mode) {
2058 case CHASE_EVENT:
2059 if (ep->code == code_to_chase) {
2060 winner = 1;
2061 }
2062 break;
2063
2064 case CHASE_DATUM:
2065 if (ep->datum == datum_to_chase) {
2066 winner = 1;
2067 }
2068 break;
2069
2070 case CHASE_TRACK:
2071 if (ep->pid->pid_value == pid_to_chase) {
2072 winner = 1;
2073 }
2074 break;
2075
2076 default:
2077 infobox("BUG", "unknown mode in chase_event_etc\n");
2078 break;
2079 }
2080
2081 if (winner) {
2082 if (ep->pid->pid_index == 0xFFFFFFFF) {
2083 ep->pid->pid_index = pids_mapped;
2084 new_pidvec[pids_mapped].pid = ep->pid;
2085 new_pidvec[pids_mapped].pid_value = ep->pid->pid_value;
2086 new_pidvec[pids_mapped].color_index = 0;
2087 pids_mapped++;
2088 if (pids_mapped == g_npids)
2089 break;
2090 }
2091 }
2092 if (srch_chase_dir == SRCH_CHASE_FORWARD)
2093 ep++;
2094 else
2095 ep--;
2096 }
2097
2098 /* Pass 2, first-to-last, to collect stragglers */
2099 ep = g_events;
2100
2101 while (ep < g_events + g_nevents) {
2102 if (ep->pid->pid_index == 0xFFFFFFFF) {
2103 ep->pid->pid_index = pids_mapped;
2104 new_pidvec[pids_mapped].pid = ep->pid;
2105 new_pidvec[pids_mapped].pid_value = ep->pid->pid_value;
2106 new_pidvec[pids_mapped].color_index = 0;
2107 pids_mapped++;
2108 if (pids_mapped == g_npids)
2109 break;
2110 }
2111 ep++;
2112 }
2113
2114 if (pids_mapped != g_npids) {
2115 infobox("BUG", "\nDidn't map all pids in chase_event_etc\n");
2116 }
2117
2118 g_free (g_pids);
2119 g_pids = new_pidvec;
Dave Barach9c8cfd32019-02-03 10:44:47 -05002120
Dave Barach52642c32016-02-11 19:28:19 -05002121 /*
2122 * The new g_pids vector contains the "chase" sort, so we revert
Dave Barach9c8cfd32019-02-03 10:44:47 -05002123 * the pid_index mapping to an identity map
Dave Barach52642c32016-02-11 19:28:19 -05002124 */
2125 psp = g_pids;
2126
2127 for (i = 0; i < g_npids; i++) {
2128 pp = psp->pid;
2129 pp->pid_index = i;
2130 psp++;
2131 }
2132
2133 /* AutoScroll the PID axis so we show the first "chased" event */
2134 s_v1->first_pid_index = 0;
2135 GTK_ADJUSTMENT(s_view1_vsadj)->value = 0.00;
2136 gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
2137 init_track_colors();
2138 view1_display_when_idle();
2139}
2140
2141/****************************************************************************
2142* unchase_event_etc
2143* Copy g_original_pids to g_pids, revert index mapping
2144****************************************************************************/
2145static void unchase_event_etc(void)
2146{
2147 int i;
2148 pid_sort_t *psp;
2149 pid_data_t *pp;
2150
Dave Barach9c8cfd32019-02-03 10:44:47 -05002151 memcpy (g_pids, g_original_pids, sizeof(pid_sort_t)*g_npids);
Dave Barach52642c32016-02-11 19:28:19 -05002152
2153 /* Fix the pid structure index mappings */
2154 psp = g_pids;
2155
2156 for (i = 0; i < g_npids; i++) {
2157 pp = psp->pid;
2158 pp->pid_index = i;
2159 psp++;
2160 }
2161
2162 /* Scroll PID axis to the top */
2163 s_v1->first_pid_index = 0;
2164 GTK_ADJUSTMENT(s_view1_vsadj)->value = 0.00;
2165 gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
2166 init_track_colors();
2167 view1_display_when_idle();
2168}
2169
2170/****************************************************************************
2171* print_ps_header
2172* To fit a reasonable-sized landscape mode plot onto letter-size paper,
2173* scale everything by .75.
2174****************************************************************************/
2175
2176static void print_ps_header (v1_geometry_t *vp, char *filename)
2177{
2178 time_t now;
2179
2180 now = time(0);
2181
2182 fprintf(s_printfp, "%%%%!PS-Adobe-3.0 EPSF-3.0\n");
2183 fprintf(s_printfp, "%%%%Creator: G2 Event Viewer\n");
2184 fprintf(s_printfp, "%%%%Title: %s\n", filename);
2185 fprintf(s_printfp, "%%%%CreationDate: %s", ctime(&now));
2186 fprintf(s_printfp, "%%%%DocumentData: Clean7Bit\n");
2187 fprintf(s_printfp, "%%%%Origin: 0 0\n");
Dave Barach9c8cfd32019-02-03 10:44:47 -05002188 fprintf(s_printfp, "%%%%BoundingBox: 0 0 %d %d\n", vp->total_height,
Dave Barach52642c32016-02-11 19:28:19 -05002189 vp->total_width);
2190 fprintf(s_printfp, "%%%%LanguageLevel: 2\n");
2191 fprintf(s_printfp, "%%%%Pages: 1\n");
2192 fprintf(s_printfp, "%%%%Page: 1 1\n");
2193 fprintf(s_printfp, "%%%%EOF\n");
2194 fprintf(s_printfp, "/Times-Roman findfont\n");
2195 fprintf(s_printfp, "12 scalefont\n");
2196 fprintf(s_printfp, "setfont\n");
2197 fprintf(s_printfp, ".75 .75 scale\n");
2198}
2199
2200/****************************************************************************
2201* xrt
2202* Xcoordinate rotate and translate. We need to emit postscript that
2203* has a reasonable aspect ratio for printing. To do that, we rotate the
Dave Barach9c8cfd32019-02-03 10:44:47 -05002204* intended picture by 90 degrees, using the standard 2D rotation
Dave Barach52642c32016-02-11 19:28:19 -05002205* formula:
Dave Barach9c8cfd32019-02-03 10:44:47 -05002206*
Dave Barach52642c32016-02-11 19:28:19 -05002207* Xr = x*cos(theta) - y*sin(theta);
2208* Yr = x*sin(theta) + y*cos(theta);
2209*
2210* If we let theta = 90, this reduces to
2211* Xr = -y
2212* Yr = x
2213*
2214* Translate back to the origin in X by adding Ymax, yielding
2215* Xrt = Ymax - y
2216****************************************************************************/
2217
2218static inline int xrt(int x, int y)
2219{
2220 return (s_v1->total_height - y);
2221}
2222
2223static inline int yrt(int x, int y)
2224{
2225 return(x);
2226}
2227
2228/****************************************************************************
2229* print_screen_callback
2230****************************************************************************/
2231
2232static boolean print_screen_callback(char *filename)
2233{
2234 s_printfp = fopen (filename, "wt");
2235
2236 if (s_printfp == NULL)
2237 return(FALSE);
2238
2239 /*
2240 * This variable allows us to magically turn the view1 display
2241 * code into a print-driver, with a minimum of fuss. The idea is to
2242 * magically change TBOX_DRAW_XXX into TBOX_PRINT_XXX by adding
2243 * the required value, aka s_print_offset.
2244 * Make sure to fix g2.h if you mess here, or vice versa.
2245 */
2246 s_print_offset = TBOX_PRINT_PLAIN - TBOX_DRAW_PLAIN;
2247
2248 print_ps_header(s_v1, filename);
2249
2250 display_pid_axis(s_v1);
2251 display_event_data(s_v1);
2252 display_time_axis(s_v1);
2253
2254 fclose (s_printfp);
2255 s_printfp = 0;
2256 s_print_offset = 0;
2257
2258 /* For tactile feedback */
2259 view1_display_when_idle();
2260 return(TRUE);
2261}
2262
Dave Barach2c35e582017-04-03 10:22:17 -04002263int event_time_cmp (const void *a, const void *b)
2264{
2265 const event_t *e1 = a;
2266 const event_t *e2 = b;
2267
2268 if (e1->time < e2->time)
2269 return -1;
2270 else if (e1->time > e2->time)
2271 return 1;
2272 return 0;
2273}
2274
2275/****************************************************************************
2276* slew_tracks
2277****************************************************************************/
2278static void slew_tracks (v1_geometry_t *vp, enum view1_button_click which)
2279{
2280 event_t *ep;
2281 pid_sort_t *pp;
2282 int pid_index;
2283 ulonglong delta;
Dave Barach9c8cfd32019-02-03 10:44:47 -05002284
Dave Barach2c35e582017-04-03 10:22:17 -04002285 delta = (ulonglong) (vp->last_time_interval);
2286
2287 /* Make sure we don't push events to the left of the big bang */
2288 if (which == SLEW_LEFT_BUTTON) {
2289 for (ep = g_events; ep < (g_events + g_nevents); ep++) {
2290 pid_index = ep->pid->pid_index;
2291 pp = (g_pids + pid_index);
Dave Barach9c8cfd32019-02-03 10:44:47 -05002292
Dave Barach2c35e582017-04-03 10:22:17 -04002293 if (pp->selected) {
2294 if (ep->time < delta) {
Dave Barach9c8cfd32019-02-03 10:44:47 -05002295 infobox("Slew Range Error",
Dave Barach2c35e582017-04-03 10:22:17 -04002296 "\nCan't slew selected data left that far..."
2297 "\nEvents would preceed the Big Bang (t=0)...");
2298 goto out;
2299 }
2300 }
2301 }
2302 }
2303
2304 for (ep = g_events; ep < (g_events + g_nevents); ep++) {
2305 pid_index = ep->pid->pid_index;
2306 pp = (g_pids + pid_index);
2307
2308 if (pp->selected) {
2309 if (which == SLEW_LEFT_BUTTON)
2310 ep->time -= delta;
2311 else
2312 ep->time += delta;
2313 }
2314 }
2315
2316 /* Re-sort the events, to avoid screwing up the event display */
2317 qsort (g_events, g_nevents, sizeof(event_t), event_time_cmp);
2318
2319 /* De-select tracks */
2320 deselect_tracks();
2321
2322out:
2323 view1_display_when_idle();
2324}
2325
Dave Barach52642c32016-02-11 19:28:19 -05002326/****************************************************************************
Dave Barach9c8cfd32019-02-03 10:44:47 -05002327* view1_button_click_callback
Dave Barach52642c32016-02-11 19:28:19 -05002328****************************************************************************/
2329
2330static void view1_button_click_callback(GtkButton *item, gpointer data)
2331{
2332 enum view1_button_click click = (enum view1_button_click) data;
2333 event_t *ep;
2334 ulonglong event_incdec;
2335 ulonglong current_width;
2336 ulonglong zoom_delta;
2337
Dave Barach52642c32016-02-11 19:28:19 -05002338 current_width = s_v1->maxvistime - s_v1->minvistime;
2339 event_incdec = (current_width) / 3;
2340
2341 if (event_incdec == 0LL)
2342 event_incdec = 1;
2343
2344 zoom_delta = (s_v1->maxvistime - s_v1->minvistime) / 6;
2345
2346 switch(click) {
2347 case TOP_BUTTON:
2348 /* First PID to top of window */
2349 s_v1->first_pid_index = 0;
2350 GTK_ADJUSTMENT(s_view1_vsadj)->value = 0.00;
2351 gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
2352 break;
2353
2354 case BOTTOM_BUTTON:
2355 s_v1->first_pid_index = g_npids - s_v1->npids;
2356 if (s_v1->first_pid_index < 0)
2357 s_v1->first_pid_index = 0;
2358 GTK_ADJUSTMENT(s_view1_vsadj)->value = (gdouble)s_v1->first_pid_index;
2359 gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
2360 break;
2361
2362 case SNAP_BUTTON:
2363 add_snapshot();
2364 break;
2365
2366 case NEXT_BUTTON:
2367 next_snapshot();
2368 break;
2369
2370 case DEL_BUTTON:
2371 del_snapshot();
2372 break;
2373
2374 case CHASE_EVENT_BUTTON:
2375 chase_event_etc(CHASE_EVENT);
2376 break;
2377
2378 case CHASE_DATUM_BUTTON:
2379 chase_event_etc(CHASE_DATUM);
2380 break;
2381
2382 case CHASE_TRACK_BUTTON:
2383 chase_event_etc(CHASE_TRACK);
2384 break;
2385
2386 case UNCHASE_BUTTON:
2387 unchase_event_etc();
2388 break;
2389
2390 case START_BUTTON:
2391 start_button:
2392 s_v1->minvistime = 0LL;
2393 s_v1->maxvistime = current_width;
2394 recompute_hscrollbar();
2395 break;
2396
2397 case ZOOMIN_BUTTON:
2398 s_v1->minvistime += zoom_delta;
2399 s_v1->maxvistime -= zoom_delta;
2400 recompute_hscrollbar();
2401 break;
2402
2403 case SEARCH_AGAIN_BUTTON:
2404 if (s_srchcode) {
2405 event_search_internal();
2406 break;
2407 }
2408 /* NOTE FALLTHROUGH */
2409
2410 case SEARCH_BUTTON:
2411 event_search();
2412 break;
2413
Dave Barach9c8cfd32019-02-03 10:44:47 -05002414 case ANOMALY_THRESHOLD_BUTTON:
2415 anomaly_threshold();
2416 break;
2417
2418 case ANOMALY_NEXT_BUTTON:
2419 if (s_anomalycode) {
2420 anomaly_search_internal();
2421 break;
2422 }
2423 /* NOTE FALLTHROUGH */
2424
2425 case ANOMALY_BUTTON:
2426 anomaly_search();
2427 break;
2428
Dave Barach52642c32016-02-11 19:28:19 -05002429 case ZOOMOUT_BUTTON:
2430 if (zoom_delta == 0LL)
2431 zoom_delta = 1;
2432
2433 if (s_v1->minvistime >= zoom_delta) {
2434 s_v1->minvistime -= zoom_delta;
2435 s_v1->maxvistime += zoom_delta;
2436 } else {
2437 s_v1->minvistime = 0;
2438 s_v1->maxvistime += zoom_delta*2;
2439 }
Dave Barach9c8cfd32019-02-03 10:44:47 -05002440
2441 if ((s_v1->maxvistime - s_v1->minvistime) * 8 >
Dave Barach52642c32016-02-11 19:28:19 -05002442 g_events[g_nevents-1].time * 9) {
2443 s_v1->minvistime = 0;
2444 s_v1->maxvistime = g_events[g_nevents-1].time * 9 / 8;
Dave Baracha4bab9d2017-12-14 17:21:36 -05002445 /* Single event? Make window 1s wide... */
2446 if (g_nevents == 1)
Dave Barach9c8cfd32019-02-03 10:44:47 -05002447 s_v1->maxvistime = 1000000;
Dave Baracha4bab9d2017-12-14 17:21:36 -05002448
Dave Barach52642c32016-02-11 19:28:19 -05002449 }
2450 recompute_hscrollbar();
2451 break;
2452
2453 case END_BUTTON:
2454 ep = (g_events + g_nevents - 1);
2455 s_v1->maxvistime = ep->time + event_incdec/3;
2456 s_v1->minvistime = s_v1->maxvistime - current_width;
2457 if (s_v1->minvistime > s_v1->maxvistime)
2458 goto start_button;
2459 recompute_hscrollbar();
2460 break;
2461
2462 case MORE_TRACES_BUTTON:
2463 /* Reduce the strip height to fit more traces on screen */
2464 s_v1->strip_height -= 1;
2465
2466 if (s_v1->strip_height < 1) {
2467 s_v1->strip_height = 1;
2468 }
2469
2470 /* Recalculate the number of strips on the screen */
Dave Barach9c8cfd32019-02-03 10:44:47 -05002471 s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) /
Dave Barach52642c32016-02-11 19:28:19 -05002472 s_v1->strip_height;
2473 recompute_vscrollbar();
2474 break;
2475
2476 case LESS_TRACES_BUTTON:
2477 /* Increase the strip height to fit fewer on the screen */
2478 s_v1->strip_height += 1;
2479 if (s_v1->strip_height > 80) {
2480 s_v1->strip_height = 80;
2481 }
2482
2483 /* Recalculate the number of strips on the screen */
Dave Barach9c8cfd32019-02-03 10:44:47 -05002484 s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) /
Dave Barach52642c32016-02-11 19:28:19 -05002485 s_v1->strip_height;
2486 recompute_vscrollbar();
2487 break;
2488
2489 case FORWARD_BUTTON:
2490 srch_chase_dir = SRCH_CHASE_FORWARD;
2491 gtk_widget_hide (s_view1_forward_button);
2492 gtk_widget_show (s_view1_backward_button);
2493 break;
2494
2495 case BACKWARD_BUTTON:
2496 srch_chase_dir = SRCH_CHASE_BACKWARD;
2497 gtk_widget_show (s_view1_forward_button);
2498 gtk_widget_hide (s_view1_backward_button);
2499 break;
2500
2501 case SUMMARY_BUTTON:
2502 summary_mode = TRUE;
2503 gtk_widget_hide (s_view1_summary_button);
2504 gtk_widget_show (s_view1_nosummary_button);
2505 break;
2506
2507 case NOSUMMARY_BUTTON:
2508 summary_mode = FALSE;
2509 gtk_widget_show (s_view1_summary_button);
2510 gtk_widget_hide (s_view1_nosummary_button);
2511 break;
Dave Barach2c35e582017-04-03 10:22:17 -04002512
2513 case SLEW_LEFT_BUTTON:
2514 case SLEW_RIGHT_BUTTON:
2515 if (s_v1->last_time_interval < 10e-9) {
Dave Barach9c8cfd32019-02-03 10:44:47 -05002516 infobox("slew", "\nNo time interval set...\n");
Dave Barach2c35e582017-04-03 10:22:17 -04002517 break;
2518 }
2519 slew_tracks (s_v1, click);
2520 break;
Dave Barach52642c32016-02-11 19:28:19 -05002521 }
2522
2523 view1_display_when_idle();
2524}
2525
2526/****************************************************************************
2527* view1_print_callback
2528****************************************************************************/
2529
2530void view1_print_callback (GtkToggleButton *notused, gpointer nu2)
2531{
2532 modal_dialog("Print Screen (PostScript format) to file:",
2533 "Invalid file: Print Screen to file:",
2534 "g2.ps", print_screen_callback);
2535}
2536
2537/****************************************************************************
2538* view1_hscroll
2539****************************************************************************/
2540
2541static void view1_hscroll (GtkAdjustment *adj, GtkWidget *notused)
2542{
2543 ulonglong current_width;
2544
2545 current_width = (s_v1->maxvistime - s_v1->minvistime);
2546
2547 s_v1->minvistime = (ulonglong)(adj->value);
2548 s_v1->maxvistime = s_v1->minvistime + current_width;
Dave Barach9c8cfd32019-02-03 10:44:47 -05002549
Dave Barach52642c32016-02-11 19:28:19 -05002550 view1_display_when_idle();
2551
2552#ifdef NOTDEF
2553 g_print ("adj->lower = %.2f\n", adj->lower);
2554 g_print ("adj->upper = %.2f\n", adj->upper);
2555 g_print ("adj->value = %.2f\n", adj->value);
2556 g_print ("adj->step_increment = %.2f\n", adj->step_increment);
2557 g_print ("adj->page_increment = %.2f\n", adj->page_increment);
2558 g_print ("adj->page_size = %.2f\n", adj->page_size);
2559#endif
2560}
2561
2562/****************************************************************************
2563* view1_vscroll
2564****************************************************************************/
2565
2566static void view1_vscroll (GtkAdjustment *adj, GtkWidget *notused)
2567{
2568 s_v1->first_pid_index = (int)adj->value;
2569 view1_display_when_idle();
2570}
2571
2572void set_pid_ax_width(int width)
2573{
2574 s_v1->pid_ax_width = width;
2575 view1_display_when_idle();
2576}
2577
2578/****************************************************************************
2579* view1_init
2580****************************************************************************/
2581
2582void view1_init(void)
2583{
Dave Barach52642c32016-02-11 19:28:19 -05002584 c_view1_draw_width = atol(getprop_default("drawbox_width", "700"));
2585 c_view1_draw_height = atol(getprop_default("drawbox_height", "400"));
2586
2587 s_v1->pid_ax_width = 80;
2588 s_v1->time_ax_height = 80;
2589 s_v1->time_ax_spacing = 100;
2590 s_v1->strip_height = 25;
2591 s_v1->pop_offset = 20;
2592 s_v1->pid_ax_offset = 34;
2593 s_v1->event_offset = 40;
2594 s_v1->total_height = c_view1_draw_height;
2595 s_v1->total_width = c_view1_draw_width;
2596 s_v1->first_pid_index = 0;
Dave Barach9c8cfd32019-02-03 10:44:47 -05002597 s_v1->anomaly_threshold_stddevs =
2598 atof(getprop_default("anomaly_threshold_stddevs", "2.5"));
2599 s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) /
Dave Barach52642c32016-02-11 19:28:19 -05002600 s_v1->strip_height;
2601
2602 s_v1->minvistime = 0;
2603 s_v1->maxvistime = 200;
2604
2605 s_view1_vbox = gtk_vbox_new(FALSE, 5);
2606
2607 s_view1_hbox = gtk_hbox_new(FALSE, 5);
2608
2609 da = gtk_drawing_area_new();
Dave Barach9c8cfd32019-02-03 10:44:47 -05002610 gtk_drawing_area_size(GTK_DRAWING_AREA(da), c_view1_draw_width,
Dave Barach52642c32016-02-11 19:28:19 -05002611 c_view1_draw_height);
Dave Barach9c8cfd32019-02-03 10:44:47 -05002612
Dave Barach52642c32016-02-11 19:28:19 -05002613#ifdef NOTDEF
2614 gtk_signal_connect (GTK_OBJECT (da), "motion_notify_event",
2615 (GtkSignalFunc) motion_notify_event, NULL);
2616#endif
2617
2618 gtk_signal_connect (GTK_OBJECT (da), "expose_event",
2619 (GtkSignalFunc) expose_event, NULL);
2620
2621 gtk_signal_connect (GTK_OBJECT(da),"configure_event",
2622 (GtkSignalFunc) configure_event, NULL);
2623
2624 gtk_signal_connect (GTK_OBJECT (da), "button_press_event",
2625 (GtkSignalFunc) button_press_event, NULL);
Dave Barach9c8cfd32019-02-03 10:44:47 -05002626
Dave Barach52642c32016-02-11 19:28:19 -05002627 gtk_signal_connect (GTK_OBJECT (da), "button_release_event",
2628 (GtkSignalFunc) button_press_event, NULL);
Dave Barach9c8cfd32019-02-03 10:44:47 -05002629
Dave Barach52642c32016-02-11 19:28:19 -05002630 gtk_signal_connect (GTK_OBJECT (da), "motion_notify_event",
2631 (GtkSignalFunc) button_press_event, NULL);
Dave Barach9c8cfd32019-02-03 10:44:47 -05002632
2633 gtk_widget_set_events (da, GDK_BUTTON_PRESS_MASK
2634 | GDK_BUTTON_RELEASE_MASK | GDK_EXPOSURE_MASK
Dave Barach52642c32016-02-11 19:28:19 -05002635 | GDK_BUTTON_MOTION_MASK);
2636
2637
2638 gtk_box_pack_start(GTK_BOX(s_view1_hbox), da, TRUE, TRUE, 0);
2639
2640 g_font = gdk_font_load ("8x13");
2641 if (g_font == NULL) {
2642 g_error("Couldn't load 8x13 font...\n");
2643 }
2644 gdk_font_ref(g_font);
2645
2646 /* PID axis menu */
2647 s_view1_vmenubox = gtk_vbox_new(FALSE, 5);
2648
Dave Barach9c8cfd32019-02-03 10:44:47 -05002649 s_view1_vsadj = gtk_adjustment_new(0.0 /* initial value */,
Dave Barach52642c32016-02-11 19:28:19 -05002650 0.0 /* minimum value */,
2651 2000.0 /* maximum value */,
Dave Barach9c8cfd32019-02-03 10:44:47 -05002652 0.1 /* step increment */,
2653 10.0/* page increment */,
Dave Barach52642c32016-02-11 19:28:19 -05002654 10.0/* page size */);
2655
2656 s_view1_vscroll = gtk_vscrollbar_new (GTK_ADJUSTMENT(s_view1_vsadj));
2657
2658 gtk_signal_connect (GTK_OBJECT (s_view1_vsadj), "value-changed",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002659 GTK_SIGNAL_FUNC (view1_vscroll),
Dave Barach52642c32016-02-11 19:28:19 -05002660 (gpointer)s_view1_vscroll);
2661
2662 s_view1_topbutton = gtk_button_new_with_label("Top");
2663 s_view1_bottombutton = gtk_button_new_with_label("Bottom");
2664
2665 gtk_signal_connect (GTK_OBJECT(s_view1_topbutton), "clicked",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002666 GTK_SIGNAL_FUNC(view1_button_click_callback),
Dave Barach52642c32016-02-11 19:28:19 -05002667 (gpointer) TOP_BUTTON);
Dave Barach9c8cfd32019-02-03 10:44:47 -05002668
Dave Barach52642c32016-02-11 19:28:19 -05002669 gtk_signal_connect (GTK_OBJECT(s_view1_bottombutton), "clicked",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002670 GTK_SIGNAL_FUNC(view1_button_click_callback),
Dave Barach52642c32016-02-11 19:28:19 -05002671 (gpointer) BOTTOM_BUTTON);
2672
2673 /* More Traces button and Less Traces button */
2674 s_view1_more_traces_button = gtk_button_new_with_label("More Traces");
2675 s_view1_less_traces_button = gtk_button_new_with_label("Less Traces");
2676 gtk_signal_connect (GTK_OBJECT(s_view1_more_traces_button), "clicked",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002677 GTK_SIGNAL_FUNC(view1_button_click_callback),
Dave Barach52642c32016-02-11 19:28:19 -05002678 (gpointer) MORE_TRACES_BUTTON);
2679 gtk_signal_connect (GTK_OBJECT(s_view1_less_traces_button), "clicked",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002680 GTK_SIGNAL_FUNC(view1_button_click_callback),
Dave Barach52642c32016-02-11 19:28:19 -05002681 (gpointer) LESS_TRACES_BUTTON);
Dave Barach9c8cfd32019-02-03 10:44:47 -05002682
Dave Barach52642c32016-02-11 19:28:19 -05002683#ifdef NOTDEF
2684 /* Trick to bottom-justify the menu: */
2685 s_view1_pad1 = gtk_vbox_new(FALSE, 0);
2686 gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_pad1,
2687 TRUE, FALSE, 0);
2688
2689#endif
Dave Barach9c8cfd32019-02-03 10:44:47 -05002690
Dave Barach52642c32016-02-11 19:28:19 -05002691 gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_topbutton,
2692 FALSE, FALSE, 0);
2693
2694 gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_vscroll,
2695 TRUE, TRUE, 0);
Dave Barach9c8cfd32019-02-03 10:44:47 -05002696
Dave Barach52642c32016-02-11 19:28:19 -05002697 gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_bottombutton,
2698 FALSE, FALSE, 0);
2699
2700 gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_more_traces_button,
2701 FALSE, FALSE, 0);
Dave Barach9c8cfd32019-02-03 10:44:47 -05002702
Dave Barach52642c32016-02-11 19:28:19 -05002703 gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_less_traces_button,
2704 FALSE, FALSE, 0);
Dave Barach9c8cfd32019-02-03 10:44:47 -05002705
Dave Barach52642c32016-02-11 19:28:19 -05002706 gtk_box_pack_start (GTK_BOX(s_view1_hbox), s_view1_vmenubox,
2707 FALSE, FALSE, 0);
2708
2709 /* Time axis menu */
2710
2711 s_view1_hmenubox = gtk_hbox_new(FALSE, 5);
Dave Barach9c8cfd32019-02-03 10:44:47 -05002712
Dave Barach52642c32016-02-11 19:28:19 -05002713 s_view1_startbutton = gtk_button_new_with_label("Start");
2714
2715 s_view1_zoominbutton = gtk_button_new_with_label("ZoomIn");
2716
2717 s_view1_searchbutton = gtk_button_new_with_label("Search");
Dave Barach52642c32016-02-11 19:28:19 -05002718 s_view1_srchagainbutton = gtk_button_new_with_label("Search Again");
2719
Dave Barach9c8cfd32019-02-03 10:44:47 -05002720 s_view1_anomalybutton = gtk_button_new_with_label("Anomaly");
2721 s_view1_anomalynextbutton = gtk_button_new_with_label("Next Anomaly");
2722 s_view1_anomalythresholdbutton =
2723 gtk_button_new_with_label ("Anomaly Threshold");
2724
Dave Barach52642c32016-02-11 19:28:19 -05002725 s_view1_zoomoutbutton = gtk_button_new_with_label("ZoomOut");
2726
2727 s_view1_endbutton = gtk_button_new_with_label("End");
2728
2729 gtk_signal_connect (GTK_OBJECT(s_view1_startbutton), "clicked",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002730 GTK_SIGNAL_FUNC(view1_button_click_callback),
Dave Barach52642c32016-02-11 19:28:19 -05002731 (gpointer) START_BUTTON);
Dave Barach9c8cfd32019-02-03 10:44:47 -05002732
Dave Barach52642c32016-02-11 19:28:19 -05002733 gtk_signal_connect (GTK_OBJECT(s_view1_zoominbutton), "clicked",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002734 GTK_SIGNAL_FUNC(view1_button_click_callback),
Dave Barach52642c32016-02-11 19:28:19 -05002735 (gpointer) ZOOMIN_BUTTON);
Dave Barach9c8cfd32019-02-03 10:44:47 -05002736
Dave Barach52642c32016-02-11 19:28:19 -05002737 gtk_signal_connect (GTK_OBJECT(s_view1_searchbutton), "clicked",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002738 GTK_SIGNAL_FUNC(view1_button_click_callback),
Dave Barach52642c32016-02-11 19:28:19 -05002739 (gpointer) SEARCH_BUTTON);
Dave Barach9c8cfd32019-02-03 10:44:47 -05002740
Dave Barach52642c32016-02-11 19:28:19 -05002741 gtk_signal_connect (GTK_OBJECT(s_view1_srchagainbutton), "clicked",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002742 GTK_SIGNAL_FUNC(view1_button_click_callback),
Dave Barach52642c32016-02-11 19:28:19 -05002743 (gpointer) SEARCH_AGAIN_BUTTON);
Dave Barach9c8cfd32019-02-03 10:44:47 -05002744
2745 gtk_signal_connect (GTK_OBJECT(s_view1_anomalybutton), "clicked",
2746 GTK_SIGNAL_FUNC(view1_button_click_callback),
2747 (gpointer) ANOMALY_BUTTON);
2748
2749 gtk_signal_connect (GTK_OBJECT(s_view1_anomalynextbutton), "clicked",
2750 GTK_SIGNAL_FUNC(view1_button_click_callback),
2751 (gpointer) ANOMALY_NEXT_BUTTON);
2752
2753 gtk_signal_connect (GTK_OBJECT(s_view1_anomalythresholdbutton),
2754 "clicked", GTK_SIGNAL_FUNC(view1_button_click_callback),
2755 (gpointer) ANOMALY_THRESHOLD_BUTTON);
2756
Dave Barach52642c32016-02-11 19:28:19 -05002757 gtk_signal_connect (GTK_OBJECT(s_view1_zoomoutbutton), "clicked",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002758 GTK_SIGNAL_FUNC(view1_button_click_callback),
Dave Barach52642c32016-02-11 19:28:19 -05002759 (gpointer) ZOOMOUT_BUTTON);
Dave Barach9c8cfd32019-02-03 10:44:47 -05002760
Dave Barach52642c32016-02-11 19:28:19 -05002761 gtk_signal_connect (GTK_OBJECT(s_view1_endbutton), "clicked",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002762 GTK_SIGNAL_FUNC(view1_button_click_callback),
Dave Barach52642c32016-02-11 19:28:19 -05002763 (gpointer) END_BUTTON);
Dave Barach9c8cfd32019-02-03 10:44:47 -05002764
2765 s_view1_hsadj = gtk_adjustment_new(0.0 /* initial value */,
Dave Barach52642c32016-02-11 19:28:19 -05002766 0.0 /* minimum value */,
2767 2000.0 /* maximum value */,
Dave Barach9c8cfd32019-02-03 10:44:47 -05002768 0.1 /* step increment */,
2769 10.0/* page increment */,
Dave Barach52642c32016-02-11 19:28:19 -05002770 10.0/* page size */);
2771
2772 s_view1_hscroll = gtk_hscrollbar_new (GTK_ADJUSTMENT(s_view1_hsadj));
2773
2774 gtk_signal_connect (GTK_OBJECT (s_view1_hsadj), "value-changed",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002775 GTK_SIGNAL_FUNC (view1_hscroll),
Dave Barach52642c32016-02-11 19:28:19 -05002776 (gpointer)s_view1_hscroll);
2777
2778 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_startbutton,
2779 FALSE, FALSE, 0);
2780
2781 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_hscroll,
2782 TRUE, TRUE, 0);
2783
2784 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_endbutton,
2785 FALSE, FALSE, 0);
2786
2787 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_zoominbutton,
2788 FALSE, FALSE, 0);
2789
Dave Barach9c8cfd32019-02-03 10:44:47 -05002790 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_anomalybutton,
2791 FALSE, FALSE, 0);
2792 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_anomalynextbutton,
2793 FALSE, FALSE, 0);
Dave Barach52642c32016-02-11 19:28:19 -05002794 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_searchbutton,
2795 FALSE, FALSE, 0);
2796
2797 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_srchagainbutton,
2798 FALSE, FALSE, 0);
2799
2800 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_zoomoutbutton,
2801 FALSE, FALSE, 0);
2802
Dave Barach9c8cfd32019-02-03 10:44:47 -05002803 gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_hbox,
Dave Barach52642c32016-02-11 19:28:19 -05002804 TRUE, TRUE, 0);
2805
2806 gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_hmenubox,
2807 FALSE, FALSE, 0);
2808
2809
2810 s_view1_hmenubox2 = gtk_hbox_new(FALSE, 5);
2811
2812 s_view1_snapbutton = gtk_button_new_with_label("Snap");
2813
2814 s_view1_nextbutton = gtk_button_new_with_label("Next");
2815
2816 s_view1_delbutton = gtk_button_new_with_label("Del");
2817
2818 s_view1_chase_event_button = gtk_button_new_with_label("ChaseEvent");
2819
2820 s_view1_chase_datum_button = gtk_button_new_with_label("ChaseDatum");
2821
2822 s_view1_chase_track_button = gtk_button_new_with_label("ChaseTrack");
2823
2824 s_view1_unchasebutton = gtk_button_new_with_label("NoChase");
2825
2826 s_view1_forward_button = gtk_button_new_with_label("->SrchChase(is<-)");
2827 s_view1_backward_button = gtk_button_new_with_label("<-SrchChase(is->)");
2828
2829 s_view1_summary_button = gtk_button_new_with_label("Summary");
2830 s_view1_nosummary_button = gtk_button_new_with_label("NoSummary");
2831
Dave Barach2c35e582017-04-03 10:22:17 -04002832 s_view1_time_slew_left_button = gtk_button_new_with_label("<-TimeSlew");
2833 s_view1_time_slew_right_button = gtk_button_new_with_label("TimeSlew->");
2834
Dave Barach52642c32016-02-11 19:28:19 -05002835 gtk_signal_connect (GTK_OBJECT(s_view1_snapbutton), "clicked",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002836 GTK_SIGNAL_FUNC(view1_button_click_callback),
Dave Barach52642c32016-02-11 19:28:19 -05002837 (gpointer) SNAP_BUTTON);
2838
2839 gtk_signal_connect (GTK_OBJECT(s_view1_nextbutton), "clicked",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002840 GTK_SIGNAL_FUNC(view1_button_click_callback),
Dave Barach52642c32016-02-11 19:28:19 -05002841 (gpointer) NEXT_BUTTON);
2842
2843 gtk_signal_connect (GTK_OBJECT(s_view1_delbutton), "clicked",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002844 GTK_SIGNAL_FUNC(view1_button_click_callback),
Dave Barach52642c32016-02-11 19:28:19 -05002845 (gpointer) DEL_BUTTON);
2846
2847 gtk_signal_connect (GTK_OBJECT(s_view1_chase_event_button), "clicked",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002848 GTK_SIGNAL_FUNC(view1_button_click_callback),
Dave Barach52642c32016-02-11 19:28:19 -05002849 (gpointer) CHASE_EVENT_BUTTON);
2850
2851 gtk_signal_connect (GTK_OBJECT(s_view1_chase_datum_button), "clicked",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002852 GTK_SIGNAL_FUNC(view1_button_click_callback),
Dave Barach52642c32016-02-11 19:28:19 -05002853 (gpointer) CHASE_DATUM_BUTTON);
2854
2855 gtk_signal_connect (GTK_OBJECT(s_view1_chase_track_button), "clicked",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002856 GTK_SIGNAL_FUNC(view1_button_click_callback),
Dave Barach52642c32016-02-11 19:28:19 -05002857 (gpointer) CHASE_TRACK_BUTTON);
2858
2859 gtk_signal_connect (GTK_OBJECT(s_view1_unchasebutton), "clicked",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002860 GTK_SIGNAL_FUNC(view1_button_click_callback),
Dave Barach52642c32016-02-11 19:28:19 -05002861 (gpointer) UNCHASE_BUTTON);
2862
2863 gtk_signal_connect (GTK_OBJECT(s_view1_forward_button), "clicked",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002864 GTK_SIGNAL_FUNC(view1_button_click_callback),
Dave Barach52642c32016-02-11 19:28:19 -05002865 (gpointer) FORWARD_BUTTON);
2866
2867 gtk_signal_connect (GTK_OBJECT(s_view1_backward_button), "clicked",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002868 GTK_SIGNAL_FUNC(view1_button_click_callback),
Dave Barach52642c32016-02-11 19:28:19 -05002869 (gpointer) BACKWARD_BUTTON);
2870
2871 gtk_signal_connect (GTK_OBJECT(s_view1_summary_button), "clicked",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002872 GTK_SIGNAL_FUNC(view1_button_click_callback),
Dave Barach52642c32016-02-11 19:28:19 -05002873 (gpointer) SUMMARY_BUTTON);
2874
2875 gtk_signal_connect (GTK_OBJECT(s_view1_nosummary_button), "clicked",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002876 GTK_SIGNAL_FUNC(view1_button_click_callback),
Dave Barach52642c32016-02-11 19:28:19 -05002877 (gpointer) NOSUMMARY_BUTTON);
2878
Dave Barach2c35e582017-04-03 10:22:17 -04002879 gtk_signal_connect (GTK_OBJECT(s_view1_time_slew_left_button), "clicked",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002880 GTK_SIGNAL_FUNC(view1_button_click_callback),
Dave Barach2c35e582017-04-03 10:22:17 -04002881 (gpointer) SLEW_LEFT_BUTTON);
2882
2883 gtk_signal_connect (GTK_OBJECT(s_view1_time_slew_right_button), "clicked",
Dave Barach9c8cfd32019-02-03 10:44:47 -05002884 GTK_SIGNAL_FUNC(view1_button_click_callback),
Dave Barach2c35e582017-04-03 10:22:17 -04002885 (gpointer) SLEW_RIGHT_BUTTON);
2886
Dave Barach52642c32016-02-11 19:28:19 -05002887 gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_hmenubox2,
2888 FALSE, FALSE, 0);
Dave Barach9c8cfd32019-02-03 10:44:47 -05002889
Dave Barach52642c32016-02-11 19:28:19 -05002890 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_snapbutton,
2891 FALSE, FALSE, 0);
2892
2893 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_nextbutton,
2894 FALSE, FALSE, 0);
2895
2896 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_delbutton,
2897 FALSE, FALSE, 0);
2898
2899 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_chase_event_button,
2900 FALSE, FALSE, 0);
2901
2902 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_chase_datum_button,
2903 FALSE, FALSE, 0);
2904
2905 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_chase_track_button,
2906 FALSE, FALSE, 0);
2907
2908 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_unchasebutton,
2909 FALSE, FALSE, 0);
2910
2911 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_forward_button,
2912 FALSE, FALSE, 0);
2913
2914 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_backward_button,
2915 FALSE, FALSE, 0);
2916
2917 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_summary_button,
2918 FALSE, FALSE, 0);
2919
2920 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_nosummary_button,
2921 FALSE, FALSE, 0);
2922
Dave Barach9c8cfd32019-02-03 10:44:47 -05002923 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2),
Dave Barach2c35e582017-04-03 10:22:17 -04002924 s_view1_time_slew_left_button,
2925 FALSE, FALSE, 0);
2926
Dave Barach9c8cfd32019-02-03 10:44:47 -05002927 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2),
Dave Barach2c35e582017-04-03 10:22:17 -04002928 s_view1_time_slew_right_button,
2929 FALSE, FALSE, 0);
2930
Dave Barach9c8cfd32019-02-03 10:44:47 -05002931 gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2),
2932 s_view1_anomalythresholdbutton,
2933 FALSE, FALSE, 0);
2934
Dave Barach52642c32016-02-11 19:28:19 -05002935 s_view1_label = gtk_label_new(NULL);
2936
2937 gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_label,
2938 FALSE, FALSE, 0);
2939
2940 gtk_box_pack_start (GTK_BOX(g_mainhbox), s_view1_vbox,
2941 TRUE, TRUE, 0);
2942
2943 gtk_widget_show_all (s_view1_vbox);
2944 GTK_WIDGET_SET_FLAGS(da, GTK_CAN_FOCUS);
2945 gtk_widget_grab_focus(da);
2946
2947 gtk_widget_hide (s_view1_forward_button);
2948 gtk_widget_hide (summary_mode ? s_view1_summary_button
2949 : s_view1_nosummary_button);
2950
Dave Barach9c8cfd32019-02-03 10:44:47 -05002951 zi_source = gdk_bitmap_create_from_data (NULL, (char *)zi_bits, zi_width,
Dave Barach52642c32016-02-11 19:28:19 -05002952 zi_height);
2953 zi_mask = gdk_bitmap_create_from_data (NULL, (char *)zi_bkgd, zi_width,
2954 zi_height);
2955
Dave Barach9c8cfd32019-02-03 10:44:47 -05002956 zi_cursor = (GdkCursor *) gdk_cursor_new_from_pixmap (zi_source,
Dave Barach52642c32016-02-11 19:28:19 -05002957 zi_mask, &fg_black,
2958 &bg_white, zi_x_hot,
2959 zi_y_hot);
2960 gdk_pixmap_unref (zi_source);
2961 gdk_pixmap_unref (zi_mask);
2962
2963 norm_cursor = (GdkCursor *) gdk_cursor_new (GDK_TOP_LEFT_ARROW);
2964}
2965
2966/****************************************************************************
2967* line_print
2968****************************************************************************/
2969
2970void line_print (int x1, int y1, int x2, int y2)
2971{
2972 fprintf(s_printfp, "newpath\n");
Dave Barach9c8cfd32019-02-03 10:44:47 -05002973 fprintf(s_printfp, "%d %d moveto\n", xrt(x1, s_v1->total_height - y1),
Dave Barach52642c32016-02-11 19:28:19 -05002974 yrt(x1, s_v1->total_height - y1));
2975
2976 fprintf(s_printfp, "%d %d lineto\n", xrt (x2, s_v1->total_height - y2),
2977 yrt (x2, s_v1->total_height - y2));
2978 fprintf(s_printfp, "1 setlinewidth\n");
2979 fprintf(s_printfp, "stroke\n");
2980}
2981
2982/****************************************************************************
2983* tbox_print
2984****************************************************************************/
2985GdkRectangle *tbox_print (char *s, int x, int y, enum view1_tbox_fn function,
2986 GdkRectangle *rp)
2987{
2988 if (function == TBOX_PRINT_BOXED) {
2989 rp->width -= 4;
2990 }
2991
2992 if ((function == TBOX_PRINT_BOXED) ||
2993 (function == TBOX_PRINT_EVENT)) {
2994
2995 fprintf(s_printfp, "newpath\n");
2996 fprintf(s_printfp, "0 setlinewidth\n");
Dave Barach9c8cfd32019-02-03 10:44:47 -05002997 fprintf(s_printfp, "%d %d moveto\n",
Dave Barach52642c32016-02-11 19:28:19 -05002998 xrt(rp->x, s_v1->total_height - rp->y),
2999 yrt(rp->x, s_v1->total_height - rp->y));
Dave Barach9c8cfd32019-02-03 10:44:47 -05003000
3001 fprintf(s_printfp, "%d %d lineto\n",
Dave Barach52642c32016-02-11 19:28:19 -05003002 xrt (rp->x+rp->width, s_v1->total_height - rp->y),
3003 yrt (rp->x+rp->width, s_v1->total_height - rp->y));
3004
Dave Barach9c8cfd32019-02-03 10:44:47 -05003005 fprintf(s_printfp, "%d %d lineto\n",
Dave Barach52642c32016-02-11 19:28:19 -05003006 xrt(rp->x+rp->width, s_v1->total_height - (rp->y+rp->height)),
3007 yrt(rp->x+rp->width, s_v1->total_height - (rp->y+rp->height)));
3008
Dave Barach9c8cfd32019-02-03 10:44:47 -05003009 fprintf(s_printfp, "%d %d lineto\n",
Dave Barach52642c32016-02-11 19:28:19 -05003010 xrt(rp->x, s_v1->total_height - (rp->y+rp->height)),
3011 yrt(rp->x, s_v1->total_height - (rp->y+rp->height)));
3012
Dave Barach9c8cfd32019-02-03 10:44:47 -05003013 fprintf(s_printfp, "%d %d lineto\n",
Dave Barach52642c32016-02-11 19:28:19 -05003014 xrt(rp->x, s_v1->total_height - rp->y),
3015 yrt(rp->x, s_v1->total_height - rp->y));
3016
3017 fprintf(s_printfp, "stroke\n");
3018 }
3019
3020 if ((function == TBOX_PRINT_BOXED) ||
3021 (function == TBOX_PRINT_PLAIN)) {
3022
3023 fprintf(s_printfp, "newpath\n");
Dave Barach9c8cfd32019-02-03 10:44:47 -05003024 fprintf(s_printfp, "%d %d moveto\n",
Dave Barach52642c32016-02-11 19:28:19 -05003025 xrt(x, s_v1->total_height - (y-2)),
3026 yrt(x, s_v1->total_height - (y-2)));
3027 fprintf(s_printfp, "gsave\n");
3028 fprintf(s_printfp, "90 rotate\n");
3029 fprintf(s_printfp, "(%s) show\n", s);
3030 fprintf(s_printfp, "grestore\n");
3031 }
3032
3033 return(rp);
Dave Barach9c8cfd32019-02-03 10:44:47 -05003034}
Dave Barach52642c32016-02-11 19:28:19 -05003035
3036/****************************************************************************
Dave Barach9c8cfd32019-02-03 10:44:47 -05003037* tbox - draws an optionally boxed string whose lower lefthand
Dave Barach52642c32016-02-11 19:28:19 -05003038* corner is at (x, y). As usual, Y is backwards.
3039****************************************************************************/
3040
3041GdkRectangle *tbox (char *s, int x, int y, enum view1_tbox_fn function)
3042{
3043 static GdkRectangle update_rect;
3044 gint lbearing, rbearing, width, ascent, descent;
3045
3046 gdk_string_extents (g_font, s,
3047 &lbearing, &rbearing,
3048 &width, &ascent, &descent);
3049
3050 /*
3051 * If we have enough room to display full size events, then just
3052 * use the BOXED function instead of the EVENT function.
3053 */
3054 if (s_v1->strip_height > 9) {
3055 switch (function) {
3056 case TBOX_DRAW_EVENT: function = TBOX_DRAW_BOXED; break;
3057 case TBOX_GETRECT_EVENT: function = TBOX_GETRECT_BOXED; break;
3058 case TBOX_PRINT_EVENT: function = TBOX_PRINT_BOXED; break;
3059 default:
3060 break;
3061 /* Nothing */
3062 }
3063 }
Dave Barach9c8cfd32019-02-03 10:44:47 -05003064
Dave Barach52642c32016-02-11 19:28:19 -05003065 switch (function) {
3066 case TBOX_DRAW_BOXED:
3067 gdk_draw_rectangle (pm, da->style->white_gc, TRUE,
Dave Barach9c8cfd32019-02-03 10:44:47 -05003068 x, y - (ascent+descent+3), width + 2,
Dave Barach52642c32016-02-11 19:28:19 -05003069 ascent + descent + 3);
Dave Barach9c8cfd32019-02-03 10:44:47 -05003070
Dave Barach52642c32016-02-11 19:28:19 -05003071 gdk_draw_rectangle (pm, da->style->black_gc, FALSE,
Dave Barach9c8cfd32019-02-03 10:44:47 -05003072 x, y - (ascent+descent+3), width + 2,
Dave Barach52642c32016-02-11 19:28:19 -05003073 ascent + descent + 3);
Dave Barach9c8cfd32019-02-03 10:44:47 -05003074
Dave Barach52642c32016-02-11 19:28:19 -05003075 gdk_draw_string (pm, g_font, da->style->black_gc,
3076 x + 1, y - 1, (const gchar *)s);
3077 /* NOTE FALLTHROUGH */
3078 case TBOX_GETRECT_BOXED:
3079 update_rect.x = x;
3080 update_rect.y = y -(ascent+descent+3);
3081 update_rect.width = width + 3;
3082 update_rect.height = ascent + descent + 4;
3083 if (function == TBOX_DRAW_BOXED)
3084 gtk_widget_draw (da, &update_rect);
3085 break;
3086
3087 case TBOX_DRAW_EVENT:
3088 /* We have a small event to draw...no text */
3089 gdk_draw_rectangle (pm, da->style->black_gc, FALSE,
3090 x, y - 1, 3, 3);
3091 /* NOTE FALLTHROUGH */
3092 case TBOX_GETRECT_EVENT:
3093 update_rect.x = x;
3094 update_rect.y = y - 1;
3095 update_rect.width = 4;
3096 update_rect.height = 4;
3097 if (function == TBOX_DRAW_EVENT)
3098 gtk_widget_draw (da, &update_rect);
3099 break;
Dave Barach9c8cfd32019-02-03 10:44:47 -05003100
3101
Dave Barach52642c32016-02-11 19:28:19 -05003102 case TBOX_DRAW_PLAIN:
Dave Barach9c8cfd32019-02-03 10:44:47 -05003103
Dave Barach52642c32016-02-11 19:28:19 -05003104 gdk_draw_string (pm, g_font, da->style->black_gc,
3105 x + 1, y - 1, (const gchar *)s);
3106 /* NOTE FALLTHROUGH */
3107 case TBOX_GETRECT_PLAIN:
3108 update_rect.x = x;
3109 update_rect.y = y -(ascent+descent+1);
3110 update_rect.width = width;
3111 update_rect.height = ascent + descent;
3112 if (function == TBOX_DRAW_PLAIN)
3113 gtk_widget_draw (da, &update_rect);
3114 break;
3115
3116 case TBOX_PRINT_BOXED:
3117 update_rect.x = x;
3118 update_rect.y = y -(ascent+descent+3);
3119 update_rect.width = width + 3;
3120 update_rect.height = ascent + descent + 4;
3121 /* note fallthrough */
3122 case TBOX_PRINT_PLAIN:
3123 return(tbox_print(s, x, y, function, &update_rect));
3124
3125 case TBOX_PRINT_EVENT:
3126 /* We have a small event box to print...no text */
3127 update_rect.x = x;
3128 update_rect.y = y - 1;
3129 update_rect.width = 4;
3130 update_rect.height = 4;
3131 return(tbox_print(s, x, y, function, &update_rect));
3132 }
3133 return(&update_rect);
3134}
3135
3136/****************************************************************************
3137* line
3138*
3139* For lines there is a primitive batching facility, that doesn't update
3140* the drawing area until the batch is complete. This is handy for drawing
3141* the pid axis and for summary mode.
3142*
3143* line_batch_mode contains the state for this:
3144*
3145* BATCH_OFF: no batching, update for every line
3146* BATCH_NEW: just entered a batch, so initialize the area to update from
3147* scratch
3148* BATCH_EXISTING: have drawn at least one line in batch mode, so the update
3149* area should only be expanded from now on to include the
3150* union of the "rectangular hull" of all lines
3151****************************************************************************/
3152
3153static enum { BATCH_OFF, BATCH_NEW, BATCH_EXISTING } line_batch_mode;
3154static int line_batch_count;
3155static int line_minx, line_miny, line_maxx, line_maxy;
3156
3157void line_batch_start (void)
3158{
3159 line_batch_mode = BATCH_NEW;
3160 line_batch_count = 0;
3161}
3162
3163void line_batch_end (void)
3164{
3165 GdkRectangle update_rect;
3166 if (line_batch_count > 0) {
3167 update_rect.x = line_minx;
3168 update_rect.y = line_miny;
3169 update_rect.width = (line_maxx - line_minx) + 1;
3170 update_rect.height = (line_maxy - line_miny) + 1;
3171 gtk_widget_draw (da, &update_rect);
3172 }
3173 line_batch_mode = BATCH_OFF;
3174}
3175
3176void line (int x1, int y1, int x2, int y2, enum view1_line_fn function)
3177{
3178 GdkRectangle update_rect;
3179 GdkGC *gc = NULL;
3180
3181 switch(function) {
3182 case LINE_DRAW_BLACK:
3183 gc = da->style->black_gc;
3184 break;
3185
3186 case LINE_DRAW_WHITE:
3187 gc = da->style->white_gc;
3188 break;
3189
3190 case LINE_PRINT:
3191 line_print (x1, y1, x2, y2);
3192 return;
3193 }
3194
3195 gdk_draw_line (pm, gc, x1, y1, x2, y2);
3196
3197 switch (line_batch_mode) {
3198 case BATCH_OFF:
3199 update_rect.x = x1;
3200 update_rect.y = y1;
3201 update_rect.width = (x2-x1) + 1;
3202 update_rect.height = (y2-y1) + 1;
3203 gtk_widget_draw (da, &update_rect);
3204 break;
3205
3206 case BATCH_NEW:
3207 line_minx = x1;
3208 line_maxx = x2;
3209 line_miny = y1;
3210 line_maxy = y2;
3211 line_batch_mode = BATCH_EXISTING;
3212 line_batch_count = 1;
3213 break;
3214
3215 case BATCH_EXISTING:
3216 if (line_minx > x1)
3217 line_minx = x1;
3218 if (line_miny > y1)
3219 line_miny = y1;
3220 if (line_maxx < x2)
3221 line_maxx = x2;
3222 if (line_maxy < y2)
3223 line_maxy = y2;
3224 line_batch_count++;
3225 break;
3226 }
3227}
3228
3229
3230/****************************************************************************
3231* display_pid_axis
3232****************************************************************************/
3233
3234static void display_pid_axis(v1_geometry_t *vp)
3235{
3236 int y, i, label_tick;
3237 int last_printed_y = -vp->strip_height;
3238 pid_sort_t *pp;
3239 int pid_index;
3240 char *label_fmt;
Dave Barach9c8cfd32019-02-03 10:44:47 -05003241 char tmpbuf [128];
Dave Barach52642c32016-02-11 19:28:19 -05003242
3243 /* No pids yet? Outta here */
3244 if (g_pids == NULL)
3245 return;
3246
3247 line_batch_start();
3248
3249 for (i = 0; i < vp->npids; i++) {
3250 pid_index = vp->first_pid_index + i;
3251 if (pid_index >= g_npids)
3252 break;
3253
Dave Barach52642c32016-02-11 19:28:19 -05003254 pp = (g_pids + pid_index);
3255
Dave Barach2c35e582017-04-03 10:22:17 -04003256 set_color(pid_index);
3257
Dave Barach52642c32016-02-11 19:28:19 -05003258 label_fmt = get_track_label(pp->pid_value);
3259 snprintf(tmpbuf, sizeof(tmpbuf)-1, label_fmt, pp->pid_value);
3260
3261 y = i*vp->strip_height + vp->pid_ax_offset;
3262
3263 /*
3264 * Have we incremented enough space to have another label not
3265 * overlap the previous label?
3266 */
3267 if (y - last_printed_y > 9) {
3268 /* Draw label */
3269 tbox(tmpbuf, 0, y +4, TBOX_DRAW_PLAIN+s_print_offset);
3270
3271 last_printed_y = y;
3272
3273 /*
3274 * And let the line stick out a bit more to indicate this label
3275 * relates to the following line.
3276 */
3277 label_tick = 4;
3278 }
3279 else {
3280 label_tick = 0;
3281 }
3282
3283 /* Draw axis line, but only if the lines aren't too close together */
3284 if (vp->strip_height > 4) {
3285 line(vp->pid_ax_width - label_tick, y+4*s_print_offset,
3286 vp->total_width, y+4*s_print_offset,
3287 LINE_DRAW_BLACK+s_print_offset);
3288 }
3289 }
3290
3291 set_color(COLOR_DEFAULT);
3292 line_batch_end();
3293}
3294
3295/****************************************************************************
3296* view1_read_events_callback
3297* New event data just showed up, reset a few things.
3298****************************************************************************/
3299
3300void view1_read_events_callback(void)
3301{
3302 int max_vis_index;
3303
3304 s_v1->first_pid_index = 0;
3305
3306 max_vis_index = 300;
3307 if (max_vis_index > g_nevents)
3308 max_vis_index = g_nevents-1;
Dave Barach9c8cfd32019-02-03 10:44:47 -05003309
Dave Barach52642c32016-02-11 19:28:19 -05003310 s_v1->minvistime = 0LL;
3311 s_v1->maxvistime = (g_events[g_nevents - 1].time * 9)/ 8;
Dave Baracha4bab9d2017-12-14 17:21:36 -05003312 /* Single event? Make the initial display 1s wide */
3313 if (g_nevents == 1)
3314 s_v1->maxvistime = 1000000;
Dave Barach52642c32016-02-11 19:28:19 -05003315 s_srchindex = 0;
3316 s_srchcode = 0;
3317 s_last_selected_event = 0;
3318
3319 init_track_colors();
3320
3321 recompute_hscrollbar();
3322 recompute_vscrollbar();
3323}
3324
3325/****************************************************************************
3326* display_event_data
3327****************************************************************************/
3328
3329static void display_event_data(v1_geometry_t *vp)
3330{
3331 int start_index;
3332 int pid_index;
3333 int x, y;
3334 event_t *ep;
3335 event_def_t *edp;
3336 double time_per_pixel;
3337 char tmpbuf[1024];
3338 GdkRectangle *print_rect;
3339 int *last_x_used;
3340
3341 /* Happens if one loads the event def header first, for example. */
3342 if (g_nevents == 0)
3343 return;
3344
3345 time_per_pixel = dtime_per_pixel(vp);
3346
3347 start_index = find_event_index (vp->minvistime);
3348
3349 /* Scrolled too far right? */
3350 if (start_index >= g_nevents)
3351 return;
3352
3353 ep = (g_events + start_index);
3354
3355 if (s_print_offset || summary_mode) {
3356 last_x_used = (int *)g_malloc0(vp->npids * sizeof(int));
3357 } else {
3358 last_x_used = NULL;
3359 }
3360
3361 line_batch_start();
3362
3363 while (ep < (g_events + g_nevents) &&
3364 (ep->time < vp->maxvistime)) {
3365 pid_index = ep->pid->pid_index;
3366 set_color(pid_index);
Dave Barach9c8cfd32019-02-03 10:44:47 -05003367
Dave Barach52642c32016-02-11 19:28:19 -05003368 /* First filter: pid out of range */
3369 if ((pid_index < vp->first_pid_index) ||
3370 (pid_index >= vp->first_pid_index + vp->npids)) {
3371 ep++;
3372 continue;
3373 }
3374
3375 /* Second filter: event hidden */
3376 edp = find_event_definition (ep->code);
3377 if (!edp->selected) {
3378 ep++;
3379 continue;
3380 }
Dave Barach9c8cfd32019-02-03 10:44:47 -05003381
Dave Barach52642c32016-02-11 19:28:19 -05003382 /* Display it... */
3383
3384 pid_index -= vp->first_pid_index;
Dave Barach9c8cfd32019-02-03 10:44:47 -05003385
Dave Barach52642c32016-02-11 19:28:19 -05003386 y = pid_index*vp->strip_height + vp->event_offset;
Dave Barach9c8cfd32019-02-03 10:44:47 -05003387
3388 x = vp->pid_ax_width +
Dave Barach52642c32016-02-11 19:28:19 -05003389 (int)(((double)(ep->time - vp->minvistime)) / time_per_pixel);
3390
3391 if (last_x_used != NULL && x < last_x_used[pid_index]) {
3392 ep++;
3393 continue;
3394 }
3395
3396 if (ep->flags & (EVENT_FLAG_SELECT | EVENT_FLAG_SEARCHRSLT)) {
3397 if (ep->flags & EVENT_FLAG_SELECT) {
3398 format_popbox_string(tmpbuf, sizeof(tmpbuf), ep, edp);
Dave Barach52642c32016-02-11 19:28:19 -05003399 } else {
Dave Barach3e07a4a2020-04-04 10:05:48 -04003400 snprintf(tmpbuf, sizeof(tmpbuf), "SEARCH RESULT");
Dave Barach52642c32016-02-11 19:28:19 -05003401 }
Dave Barach9c8cfd32019-02-03 10:44:47 -05003402 print_rect = tbox(tmpbuf, x, y - vp->pop_offset,
Dave Barach52642c32016-02-11 19:28:19 -05003403 TBOX_DRAW_BOXED+s_print_offset);
3404 line(x, y-vp->pop_offset, x, y, LINE_DRAW_BLACK+s_print_offset);
3405 if (last_x_used != NULL)
3406 last_x_used[pid_index] = x + print_rect->width;
Dave Barach9c8cfd32019-02-03 10:44:47 -05003407 }
Dave Barach52642c32016-02-11 19:28:19 -05003408 if (summary_mode) {
3409 int delta = vp->strip_height / 3;
3410 if (delta < 1)
3411 delta = 1;
3412 y = pid_index*vp->strip_height + vp->pid_ax_offset;
3413 line(x, y - delta, x, y + delta, LINE_DRAW_BLACK);
3414 last_x_used[pid_index] = x + 1;
3415 } else {
Dave Barach3e07a4a2020-04-04 10:05:48 -04003416 snprintf(tmpbuf, sizeof(tmpbuf), "%ld", ep->code);
Dave Barach52642c32016-02-11 19:28:19 -05003417 print_rect = tbox(tmpbuf, x, y, TBOX_DRAW_EVENT+s_print_offset);
3418 if (last_x_used != NULL)
3419 last_x_used[pid_index] = x + print_rect->width;
3420 }
3421
3422 ep++;
3423 }
3424 if (last_x_used)
3425 g_free(last_x_used);
3426 line_batch_end();
3427 set_color(COLOR_DEFAULT);
3428}
3429
3430/****************************************************************************
3431* display_clear
3432****************************************************************************/
3433
3434static void display_clear(void)
3435{
3436 GdkRectangle update_rect;
3437
3438 gdk_draw_rectangle (pm, da->style->white_gc, TRUE,
3439 0, 0, da->allocation.width,
3440 da->allocation.height);
3441
3442 update_rect.x = 0;
3443 update_rect.y = 0;
3444 update_rect.width = da->allocation.width;
3445 update_rect.height = da->allocation.height;
3446
3447 gtk_widget_draw (da, &update_rect);
3448}
3449
3450/****************************************************************************
3451* display_time_axis
3452****************************************************************************/
3453
3454static void display_time_axis(v1_geometry_t *vp)
3455{
3456 int x, y, i;
3457 int xoffset, nticks;
3458 char tmpbuf [128];
3459 double unit_divisor;
3460 double time;
3461 char *units;
3462 double time_per_pixel;
3463
3464 y = vp->npids * vp->strip_height + vp->pid_ax_offset;
3465
3466 x = vp->pid_ax_width;
3467
3468 nticks = (vp->total_width - vp->pid_ax_width) / vp->time_ax_spacing;
3469
3470 time_per_pixel = dtime_per_pixel(vp);
3471
3472 units = "ns";
3473 unit_divisor = 1.00;
Dave Barach9c8cfd32019-02-03 10:44:47 -05003474
Dave Barach52642c32016-02-11 19:28:19 -05003475 if ((vp->maxvistime / unit_divisor) > 1000) {
3476 units = "us";
3477 unit_divisor = 1000.00;
3478 }
3479
3480 if ((vp->maxvistime / unit_divisor) > 1000) {
3481 units = "ms";
3482 unit_divisor = 1000.00*1000.00;
3483 }
3484 if ((vp->maxvistime / unit_divisor) > 1000) {
3485 units = "s";
3486 unit_divisor = 1000.00*1000.00*1000.00;
3487 }
3488
3489 /* Draw line */
3490 line(x, y, vp->total_width, y, LINE_DRAW_BLACK+s_print_offset);
3491
3492 xoffset = 0;
Dave Barach9c8cfd32019-02-03 10:44:47 -05003493
Dave Barach52642c32016-02-11 19:28:19 -05003494 for (i = 0; i < nticks; i++) {
3495 /* Tick mark */
3496 line(x+xoffset, y-3, x+xoffset, y+3, LINE_DRAW_BLACK+s_print_offset);
3497
3498 time = (double)(x + xoffset - vp->pid_ax_width);
3499 time *= time_per_pixel;
3500 time += (double)(vp->minvistime);
3501 time /= unit_divisor;
3502
Dave Barach3e07a4a2020-04-04 10:05:48 -04003503 snprintf (tmpbuf, sizeof(tmpbuf), "%.2f%s", time, units);
Dave Barach52642c32016-02-11 19:28:19 -05003504
3505 tbox(tmpbuf, x+xoffset, y+15, TBOX_DRAW_PLAIN+s_print_offset);
Dave Barach9c8cfd32019-02-03 10:44:47 -05003506
Dave Barach52642c32016-02-11 19:28:19 -05003507 xoffset += vp->time_ax_spacing;
3508 }
3509}
3510
3511/****************************************************************************
3512* clear_scoreboard
3513* Forget about any temporary displays, they're gone now...
3514****************************************************************************/
3515
3516static void clear_scoreboard(void)
3517{
3518 s_result_up = FALSE;
3519}
3520
3521/****************************************************************************
3522* view1_display
3523****************************************************************************/
3524
3525void view1_display(void)
3526{
3527 display_clear();
3528 display_pid_axis(s_v1);
3529 display_event_data(s_v1);
3530 display_time_axis(s_v1);
3531 clear_scoreboard();
3532}
3533
3534static gint idle_tag;
3535
3536/****************************************************************************
3537* view1_display_eventually
3538****************************************************************************/
3539
3540static void view1_display_eventually(void)
3541{
3542 gtk_idle_remove(idle_tag);
3543 idle_tag = 0;
3544 view1_display();
3545}
3546
3547
3548/****************************************************************************
3549* view1_display_when_idle
3550****************************************************************************/
3551
3552void view1_display_when_idle(void)
3553{
3554 if (idle_tag == 0) {
3555 idle_tag = gtk_idle_add((GtkFunction) view1_display_eventually, 0);
3556 }
3557}
3558
3559/****************************************************************************
3560* view1_about
3561****************************************************************************/
3562
3563void view1_about (char *tmpbuf)
3564{
3565 int nsnaps;
3566 snapshot_t *snaps;
3567
Dave Barach3e07a4a2020-04-04 10:05:48 -04003568 snprintf(tmpbuf+strlen(tmpbuf), 128, "Minvistime %lld\nMaxvistime %lld\n",
Dave Barach52642c32016-02-11 19:28:19 -05003569 s_v1->minvistime, s_v1->maxvistime);
Dave Barach3e07a4a2020-04-04 10:05:48 -04003570 snprintf(tmpbuf+strlen(tmpbuf), 128, "Strip Height %d\n",
Dave Barach52642c32016-02-11 19:28:19 -05003571 s_v1->strip_height);
3572
3573 for (nsnaps = 0, snaps = s_snapshots; snaps; snaps = snaps->next) {
3574 nsnaps++;
3575 }
Dave Barach3e07a4a2020-04-04 10:05:48 -04003576 snprintf(tmpbuf+strlen(tmpbuf), 128, "%d snapshots in the ring\n", nsnaps);
Dave Barach52642c32016-02-11 19:28:19 -05003577}