#!/usr/bin/perl

# NetMonitor

# Copyright (C) 1999-2006 Mandriva
# Damien "Dam's" Krotkine
# Thierry Vignaud <tvignaud@mandriva.com>
#
# 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

use lib qw(/usr/lib/libDrakX);

use strict;
# i18n: IMPORTANT: to get correct namespace (drakx-net instead of libDrakX)
BEGIN { unshift @::textdomains, 'drakx-net' }
use standalone;     #- warning, standalone must be loaded very first, for 'explanations'

use c;
use interactive;
use mygtk2 qw(gtknew gtkset);
use ugtk2 qw(:create :helpers :wrappers);
use common;
use network::network;
use network::tools;
use POSIX;

$ugtk2::wm_icon = "/usr/share/mcc/themes/default/net_monitor-mdk.png";

if ("@ARGV" =~ /--status/) { print network::tools::connected(); exit(0) }
my $force = "@ARGV" =~ /--force/;
my $quiet = "@ARGV" =~ /--quiet/;
my $connect = "@ARGV" =~ /--connect/;
my $disconnect = "@ARGV" =~ /--disconnect/;
my ($default_intf) = "@ARGV" =~ /--defaultintf (\w+)/;

my $net = {};
network::network::read_net_conf($net);
$default_intf ||= $net->{net_interface};

if ($force) {
    $connect and network::tools::start_interface($default_intf, 1);
    $disconnect and network::tools::stop_interface($default_intf, 1);
    $connect = $disconnect = 0;
}
$quiet and exit(0);


my $window1 = ugtk2->new(N("Network Monitoring"));
$window1->{window}->signal_connect(delete_event => \&main_quit);

unless ($::isEmbedded) {
    $window1->{window}->set_position('center');
    $window1->{window}->set_title(N("Network Monitoring"));
    $window1->{window}->set_border_width(5);
}
#$::isEmbedded or $window1->{window}->set_size_request(580, 320);

my $colorr = gtkcolor(50400, 655, 20000);
my $colort = gtkcolor(55400, 55400, 655);
my $colora = gtkcolor(655, 50400, 655);
my $isconnected = -1;
my @interfaces;
my $monitor = {};
my $c_time = 0;
my $ct_tag;

my ($pixmap, $darea, $gc_lines);
my ($width, $height) = (300, 150);

my $left_border = 50;
my $grid_interval = 30;
my $arrow_space = 6;
my $arrow_size = 5;

my $cfg_file = $< ? "$ENV{HOME}/.net_monitorrc" : "/etc/sysconfig/net_monitorrc";
my %config = getVarsFromSh($cfg_file);
my $use_same_scale = text2bool($config{use_same_scale});

gtkadd($window1->{window},
       gtknew('VBox', spacing => 5, children => [
                1, gtknew('HBox', spacing => 5, children => [
                            0, my $notebook = gtknew('Notebook'),
                            1, gtknew('VBox', spacing => 5, children => [
                                        0, gtknew('Frame', text => N("Settings"), shadow_type => 'etched_out', child => 
                                                  gtknew('HBox', border_width => 5, children_tight => [
                                                            N("Connection type: "), 
                                                            my $label_cnx_type = gtknew('Label', text => "")]),
                                                 ),
                                        1, gtknew('Frame', text => N("Global statistics"), shadow_type => 'etched_out', child => 
                                                  gtknew('VBox', border_width => 5, children_tight => [
                                                            gtknew('Table', col_spacings => 1, row_spacings => 5, homogeneous => 1, children => [
                                                                             [ gtknew('Label', text => ""), gtknew('Label', text => N("Instantaneous")) , gtknew('Label', text => N("Average")) ],
                                                                             [ gtknew('WrappedLabel', text => N("Sending\nspeed:")),   my $label_st = gtknew('Label', text => ""), my $label_sta = gtknew('Label', text => N("unknown")) ],
                                                                             [ gtknew('WrappedLabel', text => N("Receiving\nspeed:")), my $label_sr = gtknew('Label', text => ""), my $label_sra = gtknew('Label', text => N("unknown")) ],
                                                                            ]),
                                                            gtknew('HSeparator'),
                                                            gtknew('HBox', border_width => 5, children_tight => [
                                                                      N("Connection\ntime: "), 
                                                                      my $label_ct = gtknew('Label', text => N("unknown")),
                                                                     ]),
                                                           ])
                                                 ),
                                       ])
                           ]),
                0, gtksignal_connect(gtkset_active(gtknew('CheckButton', text => N("Use same scale for received and transmitted")), $use_same_scale), clicked => sub { $use_same_scale = !$use_same_scale }),
                0, gtknew('HButtonBox', layout => 'edge', children_loose => [
                           my $button_connect = gtknew('Button', text => N("Wait please"), sensitive => 0, clicked => \&connection),
                           my $button_close = gtknew('Button', text => N("Close"), clicked => \&main_quit),
                          ]),
                0, my $statusbar = Gtk2::Statusbar->new
               ]),
      );

$window1->{window}->show_all;
$window1->{window}->realize;

my $gct = Gtk2::Gdk::GC->new($window1->{window}->window);
$gct->set_foreground($colort);
my $gcr = Gtk2::Gdk::GC->new($window1->{window}->window);
$gcr->set_foreground($colorr);
my $gca = Gtk2::Gdk::GC->new($window1->{window}->window);
$gca->set_foreground($colora);

$statusbar->push(1, N("Wait please, testing your connection..."));
$window1->{window}->show_all;

Glib::Timeout->add(1000, \&rescan);
my $time_tag2 = Glib::Timeout->add(1000, \&update);

update();
rescan();

gtkflush() while $isconnected == -2 || $isconnected == -1;

Glib::Source->remove($time_tag2);
$time_tag2 = Glib::Timeout->add(5000, \&update);

connection() if $connect && !$isconnected || $disconnect && $isconnected;

my $tool_pid;

$SIG{CHLD} = sub {
    my $child_pid;
    do { 
        $child_pid = waitpid(-1, POSIX::WNOHANG);
        if ($tool_pid eq $child_pid) {
            undef $tool_pid;
            $button_close->set_sensitive(1);
        }
    } while $child_pid > 0;
};


$window1->main;
main_quit();

my $during_connection;
my $first;

sub main_quit() {
    $config{use_same_scale} = bool2yesno($use_same_scale);
    setVarsInSh($cfg_file, \%config);
    ugtk2->exit(0);
}

sub connection() {
    $during_connection = 1;
    my $wasconnected = $isconnected;

    $button_connect->set_sensitive(0);
    $button_close->set_sensitive(0);
    $statusbar->pop(1);
    $statusbar->push(1, $wasconnected ? N("Disconnecting from Internet ") : N("Connecting to Internet "));
    if ($wasconnected == 0) {
	$c_time = time();
	$ct_tag = Glib::Timeout->add(1000, sub {
				       my ($sec, $min, $hour) = gmtime(time() - $c_time);
				       my $e = sprintf("%02d:%02d:%02d", $hour, $min, $sec);
				       gtkset($label_ct, text => $e); 1 });
    }
    my $nb_point = 1;
    $first = 1;

    my $_tag = Glib::Timeout->add(1000, sub {
				   $statusbar->pop(1);
				   $statusbar->push(1, ($wasconnected  == 1 ? N("Disconnecting from Internet ") : N("Connecting to Internet "))
						    . join('', map { "." } (1..$nb_point)));
				   $nb_point++;
				   if ($nb_point  < 4) { return 1 }
				   my $ret = 1;

				   my $isconnect = test_connected(0);

				   if ($nb_point < 20) {
                                       if ($first == 1)  { # first time
				           if ($isconnect == -2) { # wait for last test to finish
    				               test_connected(2); # not yet terminated, try to cancel it
				               return 1;
					   }
				           test_connected(1);  # initiates new connection test
				           $first = 0;
				           return 1;
				       }
				       if ($isconnect == -2) { return 1 } # no result yet, wait.
				       if ($isconnect == $wasconnected) {
				           # we got a test result; but the connection state did not change; retry.
                                           test_connected(1);
				           return 1;
				       }
				   }
                                   # either we got a result, or we timed out.
				   if ($isconnect != -2 || $nb_point > 20) {
				            $isconnected = $isconnect;
					    $ret = 0;
					    $statusbar->pop(1);
					    $statusbar->push(1, $wasconnected ? ($isconnected ?
									  N("Disconnection from Internet failed.") :
									  N("Disconnection from Internet complete.")) :
						     ($isconnected ?
						       N("Connection complete.") :
						       N("Connection failed.\nVerify your configuration in the Mandriva Linux Control Center."))
						    );
 				            # remove the connection time timer if connection is down or failed
 				            $isconnected or Glib::Source->remove($ct_tag);
				            my $delay = 1000;
					    # keep the message displayed longer if there is a problem.
				    	    if ($isconnected == $wasconnected) { $delay = 5000 }
				            my $_tag3 = Glib::Timeout->add($delay, sub {

						  $button_connect->set_sensitive(1);
				    		  $button_close->set_sensitive(1);
				    		  undef $during_connection;
						  update();
						  return 0;
                            });
                            }
                       return $ret;
                   });

    gtkflush();

    $tool_pid = 
      $wasconnected == 1 
	? network::tools::stop_interface($default_intf, 1)
        : network::tools::start_interface($default_intf, 1);
}

sub graph_window_width() { $width - $left_border }

sub rescan() {
    get_val();
    foreach (@interfaces) {
	my $intf = $_;
	my $recv = $monitor->{$intf}{val}[0];
	my $transmit = $monitor->{$intf}{val}[8];
	my $refr = $monitor->{$intf}{referencer};
	my $reft = $monitor->{$intf}{referencet};
	$monitor->{sr} += $recv - $refr;
	$monitor->{st} += $transmit - $reft;

	$monitor->{$intf}{recva} += $recv - $refr;
	$monitor->{$intf}{recvan}++;
	if ($monitor->{$intf}{recvan} > 9) {
	    push(@{$monitor->{$intf}{stack_ra}}, $monitor->{$intf}{recva}/10);
	    $monitor->{$intf}{recva} = $monitor->{$intf}{recvan} = 0;
	} else { push(@{$monitor->{$intf}{stack_ra}}, -1) }
	shift @{$monitor->{$intf}{stack_ra}} if @{$monitor->{$intf}{stack_ra}} > graph_window_width();

	push(@{$monitor->{$intf}{stack_r}}, $recv - $refr);
	shift @{$monitor->{$intf}{stack_r}} if @{$monitor->{$intf}{stack_r}} > graph_window_width();
	$monitor->{$intf}{labelr}->set_label(formatXiB($recv - $monitor->{$intf}{initialr}));
	$monitor->{$intf}{referencer} = $recv;

	$monitor->{$intf}{transmita} += $transmit - $reft;
	$monitor->{$intf}{transmitan}++;
	if ($monitor->{$intf}{transmitan} > 9) {
	    push(@{$monitor->{$intf}{stack_ta}}, $monitor->{$intf}{transmita}/10);
	    $monitor->{$intf}{transmita} = $monitor->{$intf}{transmitan} = 0;
	} else { push(@{$monitor->{$intf}{stack_ta}}, -1) }
	shift @{$monitor->{$intf}{stack_ta}} if @{$monitor->{$intf}{stack_ta}} > graph_window_width();

	push(@{$monitor->{$intf}{stack_t}}, $transmit - $reft);
	shift @{$monitor->{$intf}{stack_t}} if @{$monitor->{$intf}{stack_t}} > graph_window_width();
	$monitor->{$intf}{labelt}->set_label(formatXiB($transmit - $monitor->{$intf}{initialt}));
	$monitor->{$intf}{referencet} = $transmit;

	draw_monitor($monitor->{$intf}, $intf);
    }
    gtkset($label_sr, text => formatXiB($monitor->{sr}) . "/s");
    gtkset($label_st, text => formatXiB($monitor->{st}) . "/s");
    $monitor->{sra} += $monitor->{sr};
    $monitor->{sta} += $monitor->{st};
    $monitor->{nba}++;
    if ($monitor->{nba} > 9) {
	gtkset($label_sra, text => formatXiB($monitor->{sra}/10) . "/s");
	gtkset($label_sta, text => formatXiB($monitor->{sta}/10) . "/s");
	$monitor->{sra} = 0;
	$monitor->{sta} = 0;
	$monitor->{nba} = 0;
    }
    gtkset($label_cnx_type, text => translate($net->{type}));
    $monitor->{$_} = 0 foreach 'sr', 'st';
    1;
}

sub get_val() {
    my $a = cat_("/proc/net/dev");
    $a =~ s/^.*?\n.*?\n//;
    $a =~ s/^\s*lo:.*?\n//;
    my @line = split(/\n/, $a);
    require detect_devices;
    my @net_devices = detect_devices::get_net_interfaces();
    map {
	s/\s*(\w*)://;
	my $intf = $1;
        if (member($intf, @net_devices)) {
            $monitor->{$intf}{val} = [ split() ];
            $monitor->{$intf}{intf} = $intf;
            $intf;
        } else { () }
    } @line;
}

sub change_color {
    my ($color) = @_;
    my $dialog = _create_dialog(N("Color configuration"));
    $dialog->vbox->add(my $colorsel = Gtk2::ColorSelection->new);
    $colorsel->set_current_color($color);
    $dialog->add_button(N("Cancel"), 'cancel');
    $dialog->add_button(N("Ok"), 'ok');
    $dialog->show_all;
    if ($dialog->run eq 'ok') {
        $color = $colorsel->get_current_color;
    }
    $dialog->destroy;
    $color;
}

my ($scale_r, $scale_t);
$scale_r = $scale_t = $height;

sub scale_tranmistted($) { $_[0] * $scale_t }
sub scale_received($) { $_[0] * $scale_r }

sub color_button {
    my ($gc, $color) = @_;
    gtknew('Button', relief => 'none', clicked => sub {
               $color = change_color($color);
               $gc->set_rgb_fg_color($color);
               $_[0]->queue_draw;
           },
           child => gtksignal_connect(gtkshow(gtksize(gtkset(Gtk2::DrawingArea->new, width => 10, height => 10), 10, 10)),
                                      expose_event => sub { $_[0]->window->draw_rectangle($gc, 1, 0, 0, 10, 10) })
       );
}


sub update() {
    if (!$during_connection) {
        my $isconnect = test_connected(0);
        if ($isconnect != -2) {
	    $isconnected = $isconnect; # save current state
	    $isconnect = test_connected(1); # start new test
       }
    }

    my @intfs = get_val(); # get values from /proc file system
    foreach (@intfs) {
	my $intf = $_;
	if (!member($intf,@interfaces)) {
	    $default_intf ||= $intf;
	    $monitor->{$intf}{initialr} = $monitor->{$intf}{val}[0];
	    $monitor->{$intf}{initialt} = $monitor->{$intf}{val}[8];
	    $darea->{$intf} = Gtk2::DrawingArea->new;
	    $darea->{$intf}->set_events(["pointer_motion_mask"]);
	    $notebook->append_page(gtkshow(my $page = gtknew('VBox', children => [
					       0, gtknew('HBox', border_width => 5, children_tight => [
							    gtksize($darea->{$intf}, $width, $height)]),
					       0, gtknew('HBox', children => [
							    1, gtknew('VBox', children_tight => [
								      gtknew('HBox', spacing => 5, border_width => 5, children_tight => [
										   color_button($gct, $colort),
										   N("sent: "), $monitor->{$intf}{labelt} = gtknew('Label', text => "0")]),
								      gtknew('HBox', spacing => 5, border_width => 5, children_tight => [
										   color_button($gcr, $colorr),
										   N("received: "), $monitor->{$intf}{labelr} = gtknew('Label', text => "0")]),
								      gtknew('HBox', spacing => 5, border_width => 5, children_tight => [
										   color_button($gca, $colora),
										   N("average")])
								     ]),
							    0, gtknew('VBox', border_width => 5, children_tight => [
							      gtknew('Frame', text => N("Local measure"), shadow_type => 'etched_out', child => 
								     gtknew('VBox', border_width => 5, children_tight => [
									       gtknew('HBox', children_tight => [
											 N("sent: "),
											 my $measure_t = gtknew('Label', text => "0")
											]),
									       gtknew('HBox', children_tight => [
											 N("received: "),
											 my $measure_r = gtknew('Label', text => "0")
											])
									      ])
									)
									])
							  ])
					       ])),
				   gtknew('Label', text => $intf));
	    $monitor->{$intf}{page} = $notebook->page_num($page);
	    $darea->{$intf}->realize;
	    $pixmap->{$intf} = Gtk2::Gdk::Pixmap->new($darea->{$intf}->window, $width, $height, $darea->{$intf}->window->get_depth);
	    $monitor->{$intf}{referencer} = $monitor->{$intf}{val}[0];
	    $monitor->{$intf}{referencet} = $monitor->{$intf}{val}[8];
	    $pixmap->{$intf}->draw_rectangle($darea->{$intf}->style->black_gc, 1, 0, 0, $width, $height);
	    $darea->{$intf}->signal_connect(motion_notify_event => sub { 
                                    my (undef, $e) = @_;
                                    my $x = $e->x - 50;
                                    my $received    = $x >= 0 ? $monitor->{$intf}{stack_r}[$x] : 0;
                                    my $transmitted = $x >= 0 ? $monitor->{$intf}{stack_t}[$x] : 0;
                                    gtkset($measure_r, text => formatXiB($received));
                                    gtkset($measure_t, text => formatXiB($transmitted));
                                });
	    $darea->{$intf}->signal_connect(expose_event => sub {
                         $darea->{$intf}->window->draw_drawable($darea->{$intf}->style->bg_gc('normal'), $pixmap->{$intf}, 0, 0, 0, 0, $width, $height);
                     });
            $gc_lines->{$intf} = Gtk2::Gdk::GC->new($darea->{$intf}->window);
            $gc_lines->{$intf}->set_foreground($darea->{$intf}->style->white);
            $gc_lines->{$intf}->set_line_attributes(1, 'on-off-dash', 'not-last', 'round');

	}
    }
    foreach (@interfaces) {
	my $intf = $_;
     $notebook->remove_page($monitor->{$intf}{page}) unless member($intf,@intfs);
    }
    if (@intfs && !@interfaces) {
	#- select the default interface at start
	for (my $num_p = 0; $num_p < $notebook->get_n_pages; $num_p++) {
	    if ($notebook->get_tab_label_text($notebook->get_nth_page($num_p)) eq $default_intf) {
		$notebook->set_current_page($num_p);
		last;
	    }
	}
    }
    @interfaces = @intfs;
	if ($isconnected != -2 && $isconnected != -1 && !$during_connection) {
	    if ($isconnected == 1 && !in_ifconfig($net->{net_interface})) {
		$isconnected = 0;
		$statusbar->pop(1);
		$statusbar->push(1, N("Warning, another internet connection has been detected, maybe using your network"));
	    } else {
		#- translators : $net->{type} is the type of network connection (modem, adsl...)
		$statusbar->pop(1);
		$statusbar->push(1, $isconnected == 1 ? N("Connected") : N("Not connected"));
	    }
            $button_connect->set_sensitive(1);
            $button_connect->set("label", $isconnected == 1 ? N("Disconnect %s", translate($net->{type})) : N("Connect %s", $net->{type}));
	}
    unless ($default_intf || @interfaces) {
	$button_connect->set_sensitive(0);
	$button_connect->set("label", N("No internet connection configured"));
    }
    1;
}

sub in_ifconfig {
    my ($intf) = @_;
    -x '/sbin/ifconfig' or return 1;
    $intf eq '' and return 1;
    `/sbin/ifconfig` =~ /$intf/;
}

sub draw_monitor {
    my ($o, $intf) = @_;
    defined $darea->{$intf} or return;
    my $gcl = $gc_lines->{$intf};
    my $pixmap = $pixmap->{$intf};
    my $gc = $darea->{$intf}->style->white_gc;
    # fix race on ugtk2->exit that causes a crash (#33023)
    return 0 if !$gc;
    $pixmap->draw_rectangle($darea->{$intf}->style->black_gc, 1, 0, 0, $width, $height);
    my $maxr = 0;
    foreach (@{$o->{stack_r}}) { $maxr = $_ if $_ > $maxr }
    my $maxt = 0;
    foreach (@{$o->{stack_t}}) { $maxt = $_ if $_ > $maxt }

    my ($graph_maxr, $graph_maxt);
    if ($use_same_scale) {
        $graph_maxr = $graph_maxt = ($maxr + $maxt)/2;
    } else {
        $graph_maxr = $maxr;
        $graph_maxt = $maxt;
    }
    $scale_r = ($height/2) / max($graph_maxr, 1);
    $scale_t = ($height/2) / max($graph_maxt, 1);

    my $step = $left_border - 1;
    foreach (@{$o->{stack_t}}) {
	$pixmap->draw_rectangle($gct, 1, $step, 0, 1, scale_tranmistted($_));
	$step++;
    }
    $step = $left_border - 1;
    my ($av1, $av2, $last_a);
    foreach (@{$o->{stack_ta}}) {
	if ($_ != -1) {
	    if (!defined $av1) { $av1 = $_ } else { defined $av2 or $av2 = $_ }
	    if ($av1 && $av2) {
		$pixmap->draw_line($gca, $step-15, scale_tranmistted($av1), $step-5, scale_tranmistted($av2));
		$av1 = $av2;
		undef $av2;
		$last_a = $step - $left_border + 1;
	    }
	}
	$step++;
    }
    $step = $left_border - 1;
    foreach (@{$o->{stack_r}}) {
	$pixmap->draw_rectangle($gcr, 1, $step, $height-scale_received($_), 1, scale_received($_));
	$step++;
    }
    $step = $left_border - 1;
    $av1 = $av2 = undef;
    foreach (@{$o->{stack_ra}}) {
	if ($_ != -1) {
	    if (!defined $av1) { $av1 = $_ } else { defined $av2 or $av2 = $_ }
	    if (defined $av1 && defined $av2) {
		$pixmap->draw_line($gca, $step-15, $height-scale_received($av1), $step-5, $height-scale_received($av2));
		$av1 = $av2;
		undef $av2;
	    }
	}
	$step++;
    }

    my ($pix_maxr, $pix_maxt);
    if ($last_a) {
        $pix_maxr = $height - scale_received(@{$o->{stack_ra}}[$last_a]);
        $pix_maxt = scale_tranmistted(@{$o->{stack_ta}}[$last_a]);
    } else {
        $pix_maxr = $height - scale_received(@{$o->{stack_r}}[@{$o->{stack_r}}-1]);
        $pix_maxt = scale_tranmistted(@{$o->{stack_t}}[@{$o->{stack_t}}-1]);
    }

    my $x_l = $arrow_size + 1;
    my $y_l;

    #- "transmitted" arrow
    $y_l = max($arrow_space, min($pix_maxt, $pix_maxr - 2*$arrow_size - $arrow_space));
    $pixmap->draw_line($gct, $x_l, 0, $x_l, $y_l);
    $pixmap->draw_line($gct, $x_l-1, 0, $x_l-1, $y_l);
    $pixmap->draw_line($gct, $x_l+1, 0, $x_l+1, $y_l);
    $pixmap->draw_polygon($gct, 1, $x_l-$arrow_size, $y_l, $x_l+$arrow_size, $y_l, $x_l, $y_l+$arrow_size);

    #- "received" arrow
    $y_l = min($height - $arrow_space, max($pix_maxr, $y_l + 2*$arrow_size + $arrow_space));
    $pixmap->draw_line($gcr, $x_l, $height, $x_l, $y_l);
    $pixmap->draw_line($gcr, $x_l-1, $height, $x_l-1, $y_l);
    $pixmap->draw_line($gcr, $x_l+1, $height, $x_l+1, $y_l);
    $pixmap->draw_polygon($gcr, 1, $x_l-$arrow_size, $y_l, $x_l+$arrow_size, $y_l, $x_l, $y_l-$arrow_size);

    for (my $i = $grid_interval; $i <= $height - $grid_interval; $i += $grid_interval) {
	$pixmap->draw_line($gcl, $left_border, $i, $width, $i);
	my ($gc2, $text);
        if ($i > max($grid_interval, $use_same_scale ? $pix_maxt : $height/2)) {
	    $text = formatXiB(($height-$i)/$scale_r);
	    $gc2 = $gcr;
        } else {
	    $text = formatXiB($i/$scale_t);
	    $gc2 = $gct;
        }
        $pixmap->draw_layout($gc2, 45-string_width($darea->{$intf}, $text), $i-5, $darea->{$intf}->create_pango_layout($text));
    }
    $darea->{$intf}->queue_draw;
}


sub test_connected {
    my ($arg) = @_;
    $::testing || network::tools::test_connected($arg);
}