/*
 * wavemon - a wireless network monitoring aplication
 *
 * Copyright (c) 2001-2002 Jan Morgenstern <jan@jm-music.de>
 *
 * wavemon 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.
 *
 * wavemon 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 wavemon; see the file COPYING.  If not, write to the Free Software
 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
#include "iw_if.h"
#include "iw_nl80211.h"

/* GLOBALS */
static WINDOW *w_levels, *w_stats, *w_if, *w_info, *w_net;
static struct timer dyn_updates;
struct iw_range	range;
static struct iw_nl80211_linkstat ls;

void sampling_init(void (*sampling_handler)(int))
{
	struct itimerval i;
	div_t d = div(conf.stat_iv, 1000);	/* conf.stat_iv in msec */

	xsignal(SIGALRM, SIG_IGN);
	iw_getinf_range(conf_ifname(), &range);
	i.it_interval.tv_sec  = i.it_value.tv_sec  = d.quot;
	i.it_interval.tv_usec = i.it_value.tv_usec = d.rem * 1000;
	xsignal(SIGALRM, sampling_handler);

	(*sampling_handler)(0);
	setitimer(ITIMER_REAL, &i, NULL);
}

void sampling_do_poll(void)
{
	iw_nl80211_get_linkstat(&ls);
	iw_cache_update(&ls);
}

static void display_levels(void)
{
	static float qual, signal, noise, ssnr;
	/*
	 * FIXME: revise the scale implementation. It does not work
	 *        satisfactorily, maybe it is better to have a simple
	 *        solution using 3 levels of different colour.
	 */
	int8_t nscale[2] = { conf.noise_min, conf.noise_max },
	     lvlscale[2] = { -40, -20};
	char tmp[0x100];
	int line;
	bool noise_data_valid = iw_nl80211_have_survey_data(&ls);
	int sig_qual = -1;
	int sig_qual_max;
	int sig_level = ls.signal_avg ?: ls.signal;

	/* See comments in iw_cache_update */
	if (sig_level == 0)
		sig_level = ls.bss_signal;

	for (line = 1; line <= WH_LEVEL; line++)
		mvwclrtoborder(w_levels, line, 1);

	if (ls.bss_signal_qual) {
		/* BSS_SIGNAL_UNSPEC is scaled 0..100 */
		sig_qual     = ls.bss_signal_qual;
		sig_qual_max = 100;
	} else if (sig_level) {
		if (sig_level < -110)
			sig_qual = 0;
		else if (sig_level > -40)
			sig_qual = 70;
		else
			sig_qual = sig_level + 110;
		sig_qual_max = 70;
	}

	if (sig_qual == -1 && !sig_level && !noise_data_valid) {
		wattron(w_levels, A_BOLD);
		waddstr_center(w_levels, (WH_LEVEL + 1)/2, "NO INTERFACE DATA");
		goto done_levels;
	}

	line = 1;

	/* Noise data is rare. Use the space for spreading out. */
	if (!noise_data_valid)
		line++;

	if (sig_qual == -1) {
		line++;
	} else {
		qual = ewma(qual, sig_qual, conf.meter_decay / 100.0);

		mvwaddstr(w_levels, line++, 1, "link quality: ");
		sprintf(tmp, "%0.f%%  ", (1e2 * qual)/sig_qual_max);
		waddstr_b(w_levels, tmp);
		sprintf(tmp, "(%0.f/%d)  ", qual, sig_qual_max);
		waddstr(w_levels, tmp);

		waddbar(w_levels, line++, qual, 0, sig_qual_max, lvlscale, true);
	}

	/* Spacer */
	line++;
	if (!noise_data_valid)
		line++;

	if (sig_level != 0) {
		signal = ewma(signal, sig_level, conf.meter_decay / 100.0);

		mvwaddstr(w_levels, line++, 1, "signal level: ");
		sprintf(tmp, "%.0f dBm (%s)", signal, dbm2units(signal));
		waddstr_b(w_levels, tmp);

		waddbar(w_levels, line, signal, conf.sig_min, conf.sig_max,
			lvlscale, true);
		if (conf.lthreshold_action)
			waddthreshold(w_levels, line, signal, conf.lthreshold,
				      conf.sig_min, conf.sig_max, lvlscale, '>');
		if (conf.hthreshold_action)
			waddthreshold(w_levels, line, signal, conf.hthreshold,
				      conf.sig_min, conf.sig_max, lvlscale, '<');
	}

	line++;

	if (noise_data_valid) {
		noise = ewma(noise, ls.survey.noise, conf.meter_decay / 100.0);

		mvwaddstr(w_levels, line++, 1, "noise level:  ");
		sprintf(tmp, "%.0f dBm (%s)", noise, dbm2units(noise));
		waddstr_b(w_levels, tmp);

		waddbar(w_levels, line++, noise, conf.noise_min, conf.noise_max,
			nscale, false);
	}

	if (noise_data_valid && sig_level) {
		ssnr = ewma(ssnr, sig_level - ls.survey.noise,
				  conf.meter_decay / 100.0);

		mvwaddstr(w_levels, line++, 1, "SNR:           ");
		sprintf(tmp, "%.0f dB", ssnr);
		waddstr_b(w_levels, tmp);
	}

done_levels:
	wrefresh(w_levels);
}

static void display_stats(void)
{
	struct if_stat nstat;
	char tmp[0x100];

	if_getstat(conf_ifname(), &nstat);

	/*
	 * Interface RX stats
	 */
	mvwaddstr(w_stats, 1, 1, "RX: ");

	if (ls.rx_packets) {
		sprintf(tmp, "%'u (%s)",  ls.rx_packets,
			byte_units(ls.rx_bytes));
		waddstr_b(w_stats, tmp);
	} else {
		waddstr(w_stats, "n/a");
	}

	if (iw_nl80211_have_survey_data(&ls)) {
		if (ls.rx_bitrate[0]) {
			waddstr(w_stats, ", rate: ");
			waddstr_b(w_stats, ls.rx_bitrate);
		}

		if (ls.expected_thru) {
			if (ls.expected_thru >= 1024)
				sprintf(tmp, " (exp: %.1f MB/s)",  ls.expected_thru/1024.0);
			else
				sprintf(tmp, " (exp: %u kB/s)",  ls.expected_thru);
			waddstr(w_stats, tmp);
		}
	}

	if (ls.rx_drop_misc) {
		waddstr(w_stats, ", drop: ");
		sprintf(tmp, "%'llu", (unsigned long long)ls.rx_drop_misc);
		waddstr_b(w_stats, tmp);
	}

	wclrtoborder(w_stats);

	/*
	 * Interface TX stats
	 */
	mvwaddstr(w_stats, 2, 1, "TX: ");

	if (ls.tx_packets) {
		sprintf(tmp, "%'u (%s)",  ls.tx_packets,
			byte_units(ls.tx_bytes));
		waddstr_b(w_stats, tmp);
	} else {
		waddstr(w_stats, "n/a");
	}

	if (iw_nl80211_have_survey_data(&ls) && ls.tx_bitrate[0]) {
		waddstr(w_stats, ", rate: ");
		waddstr_b(w_stats, ls.tx_bitrate);
	}

	if (ls.tx_retries) {
		waddstr(w_stats, ", retries: ");
		sprintf(tmp, "%'u", ls.tx_retries);
		waddstr_b(w_stats, tmp);
	}

	if (ls.tx_failed) {
		waddstr(w_stats, ", failed: ");
		sprintf(tmp, "%'u", ls.tx_failed);
		waddstr_b(w_stats, tmp);
	}
	wclrtoborder(w_stats);
	wrefresh(w_stats);
}

static void display_info(WINDOW *w_if, WINDOW *w_info)
{
	struct iw_dyn_info info;
	struct iw_nl80211_ifstat ifs;
	struct iw_nl80211_reg ir;
	char tmp[0x100];
	int i;

	dyn_info_get(&info, conf_ifname(), &range);
	iw_nl80211_getifstat(&ifs);
	iw_nl80211_getreg(&ir);

	/*
	 * Interface Part
	 */
	wmove(w_if, 1, 1);
	waddstr_b(w_if, conf_ifname());
	if (range.enc_capa & IW_WPA_MASK)
		sprintf(tmp, " (%s, %s)", info.name, format_wpa(&range));
	else
		sprintf(tmp, " (%s)", info.name);
	waddstr(w_if, tmp);

	/* PHY */
	waddstr(w_if, ", phy ");
	sprintf(tmp, "%d", ifs.phy);
	waddstr_b(w_if, tmp);

	/* Regulatory domain */
	waddstr(w_if, ", reg: ");
	if (ir.region > 0) {
		waddstr_b(w_if, ir.country);
		sprintf(tmp, " (%s)", dfs_domain_name(ir.region));
		waddstr(w_if, tmp);
	} else {
		waddstr_b(w_if, "n/a");
	}

	if (ifs.ssid[0]) {
		waddstr(w_if, ", SSID: ");
		waddstr_b(w_if, ifs.ssid);
	}

	wclrtoborder(w_if);
	wrefresh(w_if);

	/*
	 * Info window:
	 */
	wmove(w_info, 1, 1);
	waddstr(w_info, "mode: ");
	waddstr_b(w_info, iftype_name(ifs.iftype));

	if (!ether_addr_is_zero(&ls.bssid)) {
		waddstr_b(w_info, ", ");

		switch (ls.status) {
		case NL80211_BSS_STATUS_ASSOCIATED:
			waddstr(w_info, "connected to: ");
			break;
		case NL80211_BSS_STATUS_AUTHENTICATED:
			waddstr(w_info, "authenticated with: ");
			break;
		case NL80211_BSS_STATUS_IBSS_JOINED:
			waddstr(w_info, "joined IBSS: ");
			break;
		default:
			waddstr(w_info, "station: ");
		}
		waddstr_b(w_info, ether_lookup(&ls.bssid));

		if (ls.status == NL80211_BSS_STATUS_ASSOCIATED) {
			waddstr_b(w_info, ",");
			waddstr(w_info, " time: ");
			waddstr_b(w_info, pretty_time(ls.connected_time));

			waddstr(w_info, ", inactive: ");
			sprintf(tmp, "%.1fs", (float)ls.inactive_time/1e3);
			waddstr_b(w_info, tmp);
		}
	}
	wclrtoborder(w_info);

	wmove(w_info, 2, 1);
	/* Frequency / channel */
	if (ifs.freq) {
		waddstr(w_info, "freq: ");
		sprintf(tmp, "%d MHz", ifs.freq);
		waddstr_b(w_info, tmp);

		/* The following condition should in theory never happen */
		if (ls.survey.freq && ls.survey.freq != ifs.freq) {
			sprintf(tmp, " [survey freq: %d MHz]", ls.survey.freq);
			waddstr(w_info, tmp);
		}

		if (ifs.freq_ctr1 && ifs.freq_ctr1 != ifs.freq) {
			waddstr(w_info, ", ctr1: ");
			sprintf(tmp, "%d MHz", ifs.freq_ctr1);
			waddstr_b(w_info, tmp);
		}
		if (ifs.freq_ctr2 && ifs.freq_ctr2 != ifs.freq_ctr1 && ifs.freq_ctr2 != ifs.freq) {
			waddstr(w_info, ", ctr2: ");
			sprintf(tmp, "%d MHz", ifs.freq_ctr2);
			waddstr_b(w_info, tmp);
		}

		waddstr(w_info, ", channel: ");
		sprintf(tmp, "%d", ieee80211_frequency_to_channel(ifs.freq));
		waddstr_b(w_info, tmp);

		if (ifs.chan_width >= 0) {
			sprintf(tmp, " (width: %s)", channel_width_name(ifs.chan_width));
			waddstr(w_info, tmp);
		} else if (ifs.chan_type >= 0) {
			sprintf(tmp, " (%s)", channel_type_name(ifs.chan_type));
			waddstr(w_info, tmp);
		}
	} else if (iw_nl80211_have_survey_data(&ls)) {
		waddstr(w_info, "freq: ");
		sprintf(tmp, "%d MHz", ls.survey.freq);
		waddstr_b(w_info, tmp);
	} else {
		waddstr(w_info, "frequency/channel: n/a");
	}
	wclrtoborder(w_info);

	/* Channel data */
	wmove(w_info, 3, 1);
	if (iw_nl80211_have_survey_data(&ls)) {
		waddstr(w_info, "channel ");
		waddstr(w_info, "active: ");
		waddstr_b(w_info, pretty_time_ms(ls.survey.time.active));

		waddstr(w_info, ", busy: ");
		waddstr_b(w_info, pretty_time_ms(ls.survey.time.busy));

		if (ls.survey.time.ext_busy) {
			waddstr(w_info, ", ext-busy: ");
			waddstr_b(w_info, pretty_time_ms(ls.survey.time.ext_busy));
		}

		waddstr(w_info, ", rx: ");
		waddstr_b(w_info, pretty_time_ms(ls.survey.time.rx));

		waddstr(w_info, ", tx: ");
		waddstr_b(w_info, pretty_time_ms(ls.survey.time.tx));

		if (ls.survey.time.scan) {
			waddstr(w_info, ", scan: ");
			waddstr_b(w_info, pretty_time_ms(ls.survey.time.scan));
		}
	} else if (ls.tx_bitrate[0] && ls.rx_bitrate[0]) {
		waddstr(w_info, "rx rate: ");
		waddstr_b(w_info, ls.rx_bitrate);

		if (ls.expected_thru) {
			if (ls.expected_thru >= 1024)
				sprintf(tmp, " (exp: %.1f MB/s)",  ls.expected_thru/1024.0);
			else
				sprintf(tmp, " (exp: %u kB/s)",  ls.expected_thru);
			waddstr(w_info, tmp);
		}
		waddstr(w_info, ", tx rate: ");
		waddstr_b(w_info, ls.tx_bitrate);
	}

	/* Beacons */
	wmove(w_info, 4, 1);

	if (ls.beacons) {
		waddstr(w_info, "beacons: ");
		sprintf(tmp, "%'llu", (unsigned long long)ls.beacons);
		waddstr_b(w_info, tmp);

		if (ls.beacon_loss) {
			waddstr(w_info, ", lost: ");
			sprintf(tmp, "%'u", ls.beacon_loss);
			waddstr_b(w_info, tmp);
		}
		waddstr(w_info, ", avg sig: ");
		sprintf(tmp, "%d dBm", (int8_t)ls.beacon_avg_sig);
		waddstr_b(w_info, tmp);

		waddstr(w_info, ", interval: ");
		sprintf(tmp, "%.1fs", (ls.beacon_int * 1024.0)/1e6);
		waddstr_b(w_info, tmp);

		waddstr(w_info, ", DTIM: ");
		sprintf(tmp, "%u", ls.dtim_period);
		waddstr_b(w_info, tmp);
	} else {
		waddstr(w_info, "station flags:");
		if (ls.cts_protection)
			waddstr_b(w_info, " CTS");
		if (ls.wme)
			waddstr_b(w_info, " WME");
		if (ls.tdls)
			waddstr_b(w_info, " TDLS");
		if (ls.mfp)
			waddstr_b(w_info, " MFP");
		if (!(ls.cts_protection | ls.wme | ls.tdls | ls.mfp))
			waddstr_b(w_info, " (none)");
		waddstr(w_info, ", preamble:");
		if (ls.long_preamble)
			waddstr_b(w_info, " long");
		else
			waddstr_b(w_info, " short");
		waddstr(w_info, ", slot:");
		if (ls.short_slot_time)
			waddstr_b(w_info, " short");
		else
			waddstr_b(w_info, " long");
	}

	if (info.cap_sens) {
		waddstr(w_info, ",  sensitivity: ");
		if (info.sens < 0)
			sprintf(tmp, "%d dBm", info.sens);
		else
			sprintf(tmp, "%d/%d", info.sens,
				range.sensitivity);
		waddstr_b(w_info, tmp);
	}

	wclrtoborder(w_info);

	wmove(w_info, 5, 1);
	waddstr(w_info, "power mgt: ");
	if (info.cap_power)
		waddstr_b(w_info, format_power(&info.power, &range));
	else
		waddstr(w_info, "n/a");

	if (info.cap_txpower && info.txpower.disabled) {
		waddstr(w_info, ",  tx-power: off");
	} else if (info.cap_txpower) {
		/*
		 * Convention: auto-selected values start with a capital
		 *             letter, otherwise with a small letter.
		 */
		if (info.txpower.fixed)
			waddstr(w_info, ",  tx-power: ");
		else
			waddstr(w_info, ",  TX-power: ");
		waddstr_b(w_info, format_txpower(&info.txpower));
	}
	wclrtoborder(w_info);

	wmove(w_info, 6, 1);
	waddstr(w_info, "retry: ");
	if (info.cap_retry)
		waddstr_b(w_info, format_retry(&info.retry, &range));
	else
		waddstr(w_info, "n/a");

	waddstr(w_info, ",  ");
	if (info.cap_rts) {
		waddstr(w_info, info.rts.fixed ? "rts/cts: " : "RTS/cts: ");
		if (info.rts.disabled)
			sprintf(tmp, "off");
		else
			sprintf(tmp, "%d B", info.rts.value);
		waddstr_b(w_info, tmp);
	} else {
		waddstr(w_info, "rts/cts: n/a");
	}

	waddstr(w_info, ",  ");
	if (info.cap_frag) {
		waddstr(w_info, info.frag.fixed ? "frag: " : "Frag: ");
		if (info.frag.disabled)
			sprintf(tmp, "off");
		else
			sprintf(tmp, "%d B", info.frag.value);
		waddstr_b(w_info, tmp);
	} else {
		waddstr(w_info, "frag: n/a");
	}
	wclrtoborder(w_info);

	wmove(w_info, 7, 1);
	waddstr(w_info, "encryption: ");
	if (info.keys) {
		int cnt = dyn_info_active_keys(&info);

		if (cnt == 0) {
			waddstr_b(w_info, "off (no key set)");
		} else if (info.active_key) {
			i = info.active_key - 1;
			waddstr_b(w_info, curtail(format_key(info.keys + i),
					  "..", MAXXLEN/2));

			if (info.keys[i].flags & IW_ENCODE_RESTRICTED)
				waddstr(w_info, ", restricted");
			if (info.keys[i].flags & IW_ENCODE_OPEN)
				waddstr(w_info, ", open");

			/* First key = default */
			if (cnt > 1 || info.active_key != 1) {
				sprintf(tmp, " [%d]", info.active_key);
				waddstr_b(w_info, tmp);
			}
			if (cnt > 1) {
				sprintf(tmp, " (%d other key%s)", cnt - 1,
					cnt == 2 ? "" : "s");
				waddstr(w_info, tmp);
			}
		} else  if (dyn_info_wep_keys(&info) == cnt) {
			waddstr_b(w_info, "off ");
			sprintf(tmp, "(%d disabled WEP key%s)", cnt,
				cnt == 1 ? "" : "s");
			waddstr(w_info, tmp);
		} else {
			uint8_t j = 0, k = 0;

			do  if (info.keys[j].size &&
				!(info.keys[j].flags & IW_ENCODE_DISABLED))
					info.keys[k++].size = info.keys[j].size;
			while (k < cnt && ++j < info.nkeys);

			if (cnt == 1)
				j = sprintf(tmp, "1 key (index #%u), ", j + 1);
			else
				j = sprintf(tmp, "%d keys with ", k);
			for (i = 0; i < k; i++)
				j += sprintf(tmp + j, "%s%d",	i ? "/" : "",
					     info.keys[i].size * 8);
			sprintf(tmp + j, " bits");
			waddstr_b(w_info, tmp);
		}
	} else if (has_net_admin_capability()) {
		waddstr(w_info, "no information available");
	} else {
		waddstr(w_info, "n/a (requires CAP_NET_ADMIN permissions)");
	}

	dyn_info_cleanup(&info);
	wclrtoborder(w_info);
	wrefresh(w_info);
}

static void display_netinfo(WINDOW *w_net)
{
	struct if_info info;
	char tmp[0x40];

	if_getinf(conf_ifname(), &info);

	wmove(w_net, 1, 1);
	wclrtoborder(w_net);
	if (getmaxy(w_net) == WH_NET_MAX) {
		waddstr(w_net, conf_ifname());

		waddstr_b(w_net, " (");
		waddstr(w_net, info.flags & IFF_UP ? "UP" : "DOWN");
		if (info.flags & IFF_RUNNING)		/* Interface RFC2863 OPER_UP	*/
			waddstr(w_net, " RUNNING");
#ifdef IFF_LOWER_UP	/* Linux 2.6.17 */
		if (info.flags & IFF_LOWER_UP)		/* Driver signals L1 up		*/
			waddstr(w_net, " LOWER_UP");
#endif
#ifdef IFF_DORMANT	/* Linux 2.6.17 */
		if (info.flags & IFF_DORMANT)		/* Driver signals dormant	*/
			waddstr(w_net, " DORMANT");
#endif
		if (info.flags & IFF_MASTER)		/* Master of a load balancer 	*/
			waddstr(w_net, " MASTER");
		if (info.flags & IFF_SLAVE)		/* Slave of a load balancer 	*/
			waddstr(w_net, " SLAVE");
		if (info.flags & IFF_POINTOPOINT)	/* Is a point-to-point link	*/
			waddstr(w_net, " POINTOPOINT");
		if (info.flags & IFF_DYNAMIC)		/* Address is volatile		*/
			waddstr(w_net, " DYNAMIC");
		if (info.flags & IFF_BROADCAST)		/* Valid broadcast address set	*/
			waddstr(w_net, " BROADCAST");
		if (info.flags & IFF_MULTICAST)		/* Supports multicast		*/
			waddstr(w_net, " MULTICAST");
		if (info.flags & IFF_ALLMULTI)		/* Receive all mcast  packets	*/
			waddstr(w_net, " ALLMULTI");
		if (info.flags & IFF_NOARP)		/* No ARP protocol		*/
			waddstr(w_net, " NOARP");
		if (info.flags & IFF_NOTRAILERS)	/* Avoid use of trailers	*/
			waddstr(w_net, " NOTRAILERS");
		if (info.flags & IFF_PROMISC)		/* Is in promiscuous mode	*/
			waddstr(w_net, " PROMISC");
		if (info.flags & IFF_DEBUG)		/* Internal debugging flag	*/
			waddstr(w_net, " DEBUG");
		waddstr_b(w_net, ")");

		wmove(w_net, 2, 1);
		wclrtoborder(w_net);
	}
	waddstr(w_net, "mac: ");
	waddstr_b(w_net, ether_lookup(&info.hwaddr));

	if (getmaxy(w_net) == WH_NET_MAX) {
		waddstr(w_net, ", qlen: ");
		sprintf(tmp, "%u", info.txqlen);
		waddstr_b(w_net, tmp);

		wmove(w_net, 3, 1);
		wclrtoborder(w_net);
	} else {
		waddstr(w_net, ", ");
	}
	waddstr(w_net, "ip: ");

	if (!info.addr.s_addr) {
		waddstr(w_net, "n/a");
	} else {
		sprintf(tmp, "%s/%u", inet_ntoa(info.addr),
				      prefix_len(&info.netmask));
		waddstr_b(w_net, tmp);

		/* only show bcast address if not set to the obvious default */
		if (info.bcast.s_addr !=
		    (info.addr.s_addr | ~info.netmask.s_addr)) {
			waddstr(w_net, ",  bcast: ");
			waddstr_b(w_net, inet_ntoa(info.bcast));
		}
	}

	/* 802.11 MTU may be greater than Ethernet MTU (1500) */
	if (info.mtu && info.mtu != ETH_DATA_LEN) {
		waddstr(w_net, ",  mtu: ");
		sprintf(tmp, "%u", info.mtu);
		waddstr_b(w_net, tmp);
	}

	wrefresh(w_net);
}

static void redraw_stat_levels(int signum)
{
	sampling_do_poll();
	display_levels();
	display_stats();
}

void scr_info_init(void)
{
	int line = 0;

	w_if	 = newwin_title(line, WH_IFACE, "Interface", true);
	line += WH_IFACE;
	w_levels = newwin_title(line, WH_LEVEL, "Levels", true);
	line += WH_LEVEL;
	w_stats	 = newwin_title(line, WH_STATS, "Statistics", true);
	line += WH_STATS;
	w_info	 = newwin_title(line, WH_INFO_MIN, "Info", true);
	line += WH_INFO_MIN;
	if (LINES >= WH_INFO_SCR_MIN + (WH_NET_MAX - WH_NET_MIN))
		w_net = newwin_title(line, WH_NET_MAX, "Network", false);
	else
		w_net = newwin_title(line, WH_NET_MAX, "Network", false);

	display_info(w_if, w_info);
	display_netinfo(w_net);
	start_timer(&dyn_updates, conf.info_iv * 1000000);
	sampling_init(redraw_stat_levels);
}

int scr_info_loop(WINDOW *w_menu)
{
	if (end_timer(&dyn_updates)) {
		display_info(w_if, w_info);
		display_netinfo(w_net);
		start_timer(&dyn_updates, conf.info_iv * 1000000);
	}
	return wgetch(w_menu);
}

void scr_info_fini(void)
{
	sampling_stop();

	delwin(w_net);
	delwin(w_info);
	delwin(w_stats);
	delwin(w_levels);
	delwin(w_if);
}
