/* mtr -- a network diagnostic tool Copyright (C) 1997,1998 Matt Kimball Changes/additions Copyright (C) 1998 R.E.Wolff@BitWizard.nl This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #ifndef NO_GTK #include #include #include "net.h" #include "dns.h" #include "mtr-gtk.h" #include "img/mtr_icon.xpm" #endif gint gtk_ping(gpointer data); extern char *Hostname; extern float WaitTime; static int tag; static GtkWidget *Pause_Button; void gtk_add_ping_timeout (void) { int dt; dt = calc_deltatime (WaitTime); tag = gtk_timeout_add(dt / 1000, gtk_ping, NULL); } void gtk_do_init(int *argc, char ***argv) { static int done = 0; if(!done) { gtk_init(argc, argv); done = 1; } } int gtk_detect(int *argc, char ***argv) { if(getenv("DISPLAY") != NULL) { /* If we do this here, gtk_init exits on an error. This happens BEFORE the user has had a chance to tell us not to use the display... */ return TRUE; } else { return FALSE; } } gint Window_destroy(GtkWidget *Window, gpointer data) { gtk_main_quit(); return FALSE; } gint Restart_clicked(GtkWidget *Button, gpointer data) { net_reset(); gtk_redraw(); return FALSE; } gint Pause_clicked(GtkWidget *Button, gpointer data) { static int paused = 0; if (paused) { gtk_add_ping_timeout (); } else { gtk_timeout_remove (tag); } paused = ! paused; gtk_redraw(); return FALSE; } /* * There is a small problem with the following code: * The timeout is canceled and removed in order to ensure that * it takes effect (consider what happens if you set the timeout to 999, * then try to undo the change); is a better approach possible? -- CMR * * What's the problem with this? (-> "I don't think so) -- REW */ gint WaitTime_changed(GtkAdjustment *Adj, GtkWidget *Button) { WaitTime = gtk_spin_button_get_value_as_float(GTK_SPIN_BUTTON(Button)); gtk_timeout_remove (tag); gtk_add_ping_timeout (); gtk_redraw(); return FALSE; } gint Host_activate(GtkWidget *Entry, gpointer data) { int addr; addr = dns_forward(gtk_entry_get_text(GTK_ENTRY(Entry))); if(addr) { net_reopen(addr); /* If we are "Paused" at this point it is usually because someone entered a non-existing host. Therefore do the go-ahead... --REW */ gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON( Pause_Button ) , 0); } else { gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON( Pause_Button ) , 1); gtk_entry_append_text( GTK_ENTRY(Entry), ": not found" ); } return FALSE; } GdkPixmap *gtk_load_pixmap(char **pixmap) { return gdk_pixmap_colormap_create_from_xpm_d(NULL, gdk_colormap_get_system(), NULL, NULL, pixmap); } void Toolbar_fill(GtkWidget *Toolbar) { GtkWidget *Button; GtkWidget *Label; GtkWidget *Entry; GtkAdjustment *Adjustment; Button = gtk_button_new_with_label("Quit"); gtk_box_pack_end(GTK_BOX(Toolbar), Button, FALSE, FALSE, 0); gtk_signal_connect(GTK_OBJECT(Button), "clicked", GTK_SIGNAL_FUNC(Window_destroy), NULL); gtk_widget_show(Button); Button = gtk_button_new_with_label("Restart"); gtk_box_pack_end(GTK_BOX(Toolbar), Button, FALSE, FALSE, 0); gtk_signal_connect(GTK_OBJECT(Button), "clicked", GTK_SIGNAL_FUNC(Restart_clicked), NULL); gtk_widget_show(Button); Pause_Button = gtk_toggle_button_new_with_label("Pause"); gtk_box_pack_end(GTK_BOX(Toolbar), Pause_Button, FALSE, FALSE, 0); gtk_signal_connect(GTK_OBJECT(Pause_Button), "clicked", GTK_SIGNAL_FUNC(Pause_clicked), NULL); gtk_widget_show(Pause_Button); /* allow root only to set zero delay */ Adjustment = (GtkAdjustment *)gtk_adjustment_new(WaitTime, getuid()==0 ? 0.00:1.00, 999.99, 1.0, 10.0, 0.0); Button = gtk_spin_button_new(Adjustment, 0.5, 2); gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(Button), TRUE); /* gtk_spin_button_set_snap_to_ticks(GTK_SPIN_BUTTON(Button), FALSE); */ /* gtk_spin_button_set_set_update_policy(GTK_SPIN_BUTTON(Button), GTK_UPDATE_IF_VALID); */ gtk_box_pack_end(GTK_BOX(Toolbar), Button, FALSE, FALSE, 0); gtk_signal_connect(GTK_OBJECT(Adjustment), "value_changed", GTK_SIGNAL_FUNC(WaitTime_changed), Button); gtk_widget_show(Button); Label = gtk_label_new("Hostname"); gtk_box_pack_start(GTK_BOX(Toolbar), Label, FALSE, FALSE, 0); gtk_widget_show(Label); Entry = gtk_entry_new(); gtk_entry_set_text(GTK_ENTRY(Entry), Hostname); gtk_signal_connect(GTK_OBJECT(Entry), "activate", GTK_SIGNAL_FUNC(Host_activate), NULL); gtk_box_pack_start(GTK_BOX(Toolbar), Entry, TRUE, TRUE, 0); gtk_widget_show(Entry); } char *Report_Text[] = { "Hostname", "Loss", "Rcv", "Snt", "Last", "Best", "Avg", "Worst", NULL }; int Report_Positions[] = { 10, 240, 280, 320, 360, 400, 440, 480, 0 }; GtkWidget *Report; GtkWidget *ReportBody; GtkWidget *GetRow(int index) { int addr; char str[256], *name; GtkWidget *Row, *Label; Row = gtk_fixed_new(); addr = net_addr(index); name = "???"; if(addr != 0) { name = dns_lookup(addr); if(!name) { /* Actually this is not neccesary: dns_lookup always returns a printable string */ name = strlongip (addr); } } Label = gtk_label_new(name); gtk_fixed_put(GTK_FIXED(Row), Label, Report_Positions[0], 0); gtk_widget_show(Label); return Row; } GtkWidget *Scrollarea_create() { GtkWidget *List; GtkWidget *scroll; int count; for(count = 0; Report_Positions[count]; count++); List = GTK_WIDGET(gtk_clist_new_with_titles(count, Report_Text)); scroll = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); for(count = 0; Report_Positions[count + 1]; count++) { gtk_clist_set_column_width(GTK_CLIST(List), count, Report_Positions[count + 1] - Report_Positions[count]); } gtk_clist_set_column_width(GTK_CLIST(List), count, 0); for(count = 1; Report_Positions[count]; count++) { gtk_clist_set_column_justification(GTK_CLIST(List), count, GTK_JUSTIFY_RIGHT); } gtk_container_add(GTK_CONTAINER(scroll), List); gtk_widget_show(List); ReportBody = List; return scroll; } void gtk_add_row(GtkWidget *List) { int at; GtkWidget *Row, *Label; Row = gtk_fixed_new(); for(at = 0; Report_Positions[at] != 0; at++) { Label = gtk_label_new("-"); if(at) { gtk_widget_set_usize(Label, 40, 0); gtk_label_set_justify(GTK_LABEL(Label), GTK_JUSTIFY_RIGHT); } gtk_fixed_put(GTK_FIXED(Row), Label, Report_Positions[at], 0); gtk_widget_show(Label); } gtk_box_pack_start(GTK_BOX(List), Row, FALSE, FALSE, 0); gtk_widget_show(Row); } void gtk_set_field(GtkCList *List, int row, int ix, char *str) { gtk_clist_set_text(List, row, ix, str); } void gtk_set_field_num(GtkCList *List, int row, int ix, char *format, int num) { char str[32]; sprintf(str, format, num); gtk_set_field(List, row, ix, str); } void gtk_update_row(GtkCList *List, int row) { int addr; char str[256], *name; GdkColor color; GdkColormap *cmap; addr = net_addr(row); name = "???"; if(addr != 0) { name = dns_lookup(addr); if(!name) { sprintf(str, "%d.%d.%d.%d", (addr >> 24) & 0xff, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff); name = str; } } cmap = gtk_widget_get_colormap(ReportBody); if (net_up(row)) { gdk_color_black(cmap, &color); } else { color.red = 0xffffff; color.green = 0; color.blue = 0; } gdk_color_alloc (cmap, &color); gtk_clist_set_foreground(List, row, &color); gtk_set_field(List, row, 0, name); gtk_set_field_num(List, row, 1, "%d%%", net_percent(row)); gtk_set_field_num(List, row, 2, "%d", net_returned(row)); gtk_set_field_num(List, row, 3, "%d", net_xmit(row)); gtk_set_field_num(List, row, 4, "%d", net_last(row)/1000); gtk_set_field_num(List, row, 5, "%d", net_best(row)/1000); gtk_set_field_num(List, row, 6, "%d", net_avg(row)/1000); gtk_set_field_num(List, row, 7, "%d", net_worst(row)/1000); } void gtk_redraw() { int at; int max = net_max(); gtk_clist_freeze(GTK_CLIST(ReportBody)); while(GTK_CLIST(ReportBody)->rows < max) { gtk_clist_append(GTK_CLIST(ReportBody), Report_Text); } while(GTK_CLIST(ReportBody)->rows > max) { gtk_clist_remove(GTK_CLIST(ReportBody), GTK_CLIST(ReportBody)->rows - 1); } for(at = 0; at < max; at++) { gtk_update_row(GTK_CLIST(ReportBody), at); } gtk_clist_thaw(GTK_CLIST(ReportBody)); } void Window_fill(GtkWidget *Window) { GtkWidget *VBox; GtkWidget *Toolbar; GtkWidget *List; gtk_window_set_title(GTK_WINDOW(Window), "My traceroute [v" VERSION "]"); gtk_window_set_wmclass(GTK_WINDOW(Window), "mtr", "Mtr"); gtk_widget_set_usize(Window, 580, 400); gtk_container_border_width(GTK_CONTAINER(Window), 10); VBox = gtk_vbox_new(FALSE, 10); Toolbar = gtk_hbox_new(FALSE, 10); Toolbar_fill(Toolbar); gtk_box_pack_start(GTK_BOX(VBox), Toolbar, FALSE, FALSE, 0); gtk_widget_show(Toolbar); List = Scrollarea_create(); gtk_box_pack_start(GTK_BOX(VBox), List, TRUE, TRUE, 0); gtk_widget_show(List); gtk_container_add(GTK_CONTAINER(Window), VBox); gtk_widget_show(VBox); } void gtk_open() { GtkWidget *Window; GdkPixmap *icon; int argc; char *args[2]; char **argv; argc = 1; argv = args; argv[0] = ""; argv[1] = NULL; gtk_do_init(&argc, &argv); Window = gtk_window_new(GTK_WINDOW_TOPLEVEL); Window_fill(Window); gtk_signal_connect_object(GTK_OBJECT(Window), "delete_event", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(Window)); gtk_signal_connect(GTK_OBJECT(Window), "destroy", GTK_SIGNAL_FUNC(Window_destroy), NULL); icon = gtk_load_pixmap(mtr_icon); gtk_widget_show(Window); gdk_window_set_icon(Window->window, NULL, icon, NULL); gdk_window_set_icon_name(Window->window, "mtr"); } void gtk_close() { } int gtk_keyaction() { return 0; } gint gtk_ping(gpointer data) { gtk_redraw(); net_send_batch(); gtk_timeout_remove (tag); gtk_add_ping_timeout (); return TRUE; } void gtk_net_data(gpointer data, gint fd, GdkInputCondition cond) { net_process_return(); } void gtk_dns_data(gpointer data, gint fd, GdkInputCondition cond) { dns_ack(); gtk_redraw(); } void gtk_loop() { gtk_add_ping_timeout (); gdk_input_add(net_waitfd(), GDK_INPUT_READ, gtk_net_data, NULL); gdk_input_add(dns_waitfd(), GDK_INPUT_READ, gtk_dns_data, NULL); gtk_main(); }