cnping

Minimal Graphical Ping Tool
git clone https://git.sinitax.com/cnlohr/cnping
Log | Files | Refs | Submodules | README | LICENSE | sfeed.txt

cnping.c (19047B)


      1//Copyright (c) 2011-2019 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose.
      2
      3#include <stdio.h>
      4#include <stdlib.h>
      5#include <math.h>
      6#include <errno.h>
      7#include <string.h>
      8#if defined( WINDOWS ) || defined( WIN32 )
      9#ifdef _MSC_VER
     10#define strdup _strdup
     11#endif
     12#include <windows.h>
     13#else
     14  #ifdef __FreeBSD__
     15    #include <sys/types.h>
     16    #include <netinet/in.h>
     17  #endif
     18#include <sys/socket.h>
     19#include <netinet/ip.h>
     20#include <netinet/ip_icmp.h>
     21#include <arpa/inet.h>
     22#include <sys/select.h>
     23#include <netdb.h>
     24#endif
     25
     26#ifndef CNFGOGL
     27#define CNFGOGL
     28#endif
     29
     30#define CNFG_IMPLEMENTATION
     31#include "rawdraw/os_generic.h"
     32#include "rawdraw/CNFG.h"
     33#include "ping.h"
     34#include "error_handling.h"
     35#include "httping.h"
     36
     37// #### Cross-plattform debugging ####
     38// Windows does not print to Console, use DebugView from SysInternals to
     39// see the output. (Setup: Computer -> Connect Local; Capture -> Capture Win32)
     40// Warning: Debugging on Windows can slow cnping down and lead to wrong measurements!
     41//#define DEBUG
     42#ifdef DEBUG
     43	char msgbuf[1024];
     44	#ifdef WIN32
     45		#define debug(...) \
     46			snprintf(msgbuf, sizeof(msgbuf), __VA_ARGS__); \
     47			OutputDebugString(msgbuf);
     48	#else
     49		#define debug(...) printf(__VA_ARGS__);
     50	#endif
     51#else
     52	// Let the compiler parse it to catch errors. Compiler will optimize away.
     53	#define debug(...) \
     54			do { if (0) fprintf(stderr, __VA_ARGS__); } while (0);
     55#endif
     56
     57unsigned frames = 0;
     58unsigned long iframeno = 0;
     59short screenx, screeny;
     60const char * pinghost;
     61float GuiYScaleFactor;
     62int GuiYscaleFactorIsConstant;
     63double globmaxtime, globmintime = 1e20;
     64double globinterval, globlast;
     65uint64_t globalrx;
     66uint64_t globallost;
     67// Ping Data. Will be overwritten with random bytes when !DEBUG
     68uint8_t pattern[8] = {0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF};
     69
     70#define PINGCYCLEWIDTH 8192
     71#define TIMEOUT 4
     72
     73double PingSendTimes[PINGCYCLEWIDTH];
     74double PingRecvTimes[PINGCYCLEWIDTH];
     75int current_cycle = 0;
     76
     77int ExtraPingSize;
     78int in_histogram_mode, in_frame_mode = 1;
     79void HandleGotPacket( int seqno, int timeout );
     80
     81#if defined( WINDOWS ) || defined( WIN32 )
     82WSADATA wsaData;
     83#endif
     84
     85
     86#define MAX_HISTO_MARKS (TIMEOUT*10000)
     87uint64_t hist_counts[MAX_HISTO_MARKS];
     88
     89void HandleNewPacket( int seqno )
     90{
     91	double Now = OGGetAbsoluteTime();
     92	PingSendTimes[seqno] = Now;
     93	PingRecvTimes[seqno] = 0;
     94	static int timeoutmark;
     95
     96	while( Now - PingSendTimes[timeoutmark] > TIMEOUT )
     97	{
     98		if( PingRecvTimes[timeoutmark] < PingSendTimes[timeoutmark] )
     99		{
    100			HandleGotPacket( timeoutmark, 1 );
    101		}
    102		timeoutmark++;
    103		if( timeoutmark >= PINGCYCLEWIDTH ) timeoutmark = 0;
    104	}
    105}
    106
    107void HandleGotPacket( int seqno, int timeout )
    108{
    109	double Now = OGGetAbsoluteTime();
    110
    111	if( timeout )
    112	{
    113		if( PingRecvTimes[seqno] < -0.5 ) return;
    114
    115		globallost++;
    116		PingRecvTimes[seqno] = -1;
    117		hist_counts[MAX_HISTO_MARKS-1]++;
    118		return;
    119	}
    120
    121	if( PingRecvTimes[seqno] >= PingSendTimes[seqno] ) return;
    122	if( PingSendTimes[seqno] < 1 )  return;
    123	if( Now - PingSendTimes[seqno] > TIMEOUT ) return;
    124
    125	PingRecvTimes[seqno] = OGGetAbsoluteTime();
    126	double Delta = PingRecvTimes[seqno] - PingSendTimes[seqno];
    127	if( Delta > globmaxtime ) { globmaxtime = Delta; }
    128	if( Delta < globmintime ) { globmintime = Delta; }
    129	int slot = Delta * 10000;
    130	if( slot >= MAX_HISTO_MARKS ) slot = MAX_HISTO_MARKS-1;
    131	if( slot < 0 ) slot = 0;
    132	hist_counts[slot]++;
    133
    134	if( globlast > 0.5 )
    135	{
    136		if( Now - globlast > globinterval ) globinterval = Now - globlast;
    137	}
    138	globlast = Now;
    139	globalrx++;
    140}
    141
    142
    143void HTTPingCallbackStart( int seqno )
    144{
    145	current_cycle = seqno;
    146	HandleNewPacket( seqno );
    147}
    148
    149void HTTPingCallbackGot( int seqno )
    150{
    151	HandleGotPacket( seqno, 0 );
    152}
    153
    154void display(uint8_t *buf, int bytes)
    155{
    156	int reqid = (buf[0] << 24) | (buf[1]<<16) | (buf[2]<<8) | (buf[3]);
    157	debug("Received ping: reqid=%d\n", reqid);
    158	reqid &= (PINGCYCLEWIDTH-1);
    159	if( memcmp( buf+4, pattern, sizeof(pattern) ) != 0 ) return;
    160	debug("Memcmp OK, checked %ld bytes, first values being %x %x %x %x\n",
    161		  (long int) sizeof(pattern), pattern[0], pattern[1], pattern[2], pattern[3])
    162	HandleGotPacket( reqid, 0 );
    163}
    164
    165int load_ping_packet( uint8_t * buffer, int bufflen )
    166{
    167	buffer[0] = current_cycle >> 24;
    168	buffer[1] = current_cycle >> 16;
    169	buffer[2] = current_cycle >> 8;
    170	buffer[3] = current_cycle >> 0;
    171
    172	memcpy( buffer+4, pattern, sizeof(pattern) );
    173
    174	if( ping_failed_to_send )
    175	{
    176		PingSendTimes[(current_cycle+PINGCYCLEWIDTH-1)&(PINGCYCLEWIDTH-1)] = 0; //Unset ping send.
    177	}
    178
    179	HandleNewPacket( current_cycle&(PINGCYCLEWIDTH-1) );
    180
    181	current_cycle++;
    182
    183	return 12 + ExtraPingSize;
    184}
    185
    186void * PingListen( void * r )
    187{
    188	listener();
    189	ERRM( "Fault on listen.\n" );
    190	exit( -2 );
    191}
    192
    193void * PingSend( void * r )
    194{
    195	do_pinger( );
    196	ERRM( "Fault on ping.\n" );
    197	exit( -1 );
    198}
    199
    200
    201
    202
    203void HandleKey( int keycode, int bDown )
    204{
    205	switch( keycode )
    206	{
    207
    208#if defined( WIN32 ) || defined( WINDOWS )
    209		case 'r':
    210		{
    211			char   lpFilename[1024];
    212			char   lpDirectory[1024];
    213			GetCurrentDirectory( 1023, lpDirectory );
    214			GetModuleFileNameA( GetModuleHandle(0), lpFilename, 1023 );
    215
    216			CreateProcessA( lpFilename, GetCommandLine(), 0, 0, 1, 0, 0, lpDirectory, 0, 0 );
    217			exit( 0 );
    218			break;
    219		}
    220#endif
    221		case 'f':
    222			if( bDown ) in_frame_mode = !in_frame_mode;
    223			if( !in_frame_mode ) in_histogram_mode = 1;
    224			break;
    225		case 'm': 
    226			if( bDown ) in_histogram_mode = !in_histogram_mode;
    227			if( !in_histogram_mode ) in_frame_mode = 1;
    228			break;
    229		case 'c':
    230			memset( hist_counts, 0, sizeof( hist_counts ) );
    231			globmaxtime = 0;
    232			globmintime = 1e20;
    233			globinterval = 0;
    234			globlast = 0;
    235			globalrx = 0;
    236			globallost = 0;
    237			break;
    238		case 'q':
    239			exit(0);
    240			break;
    241
    242	}
    243}
    244void HandleButton( int x, int y, int button, int bDown ){}
    245void HandleMotion( int x, int y, int mask ){}
    246void HandleDestroy() { exit(0); }
    247
    248
    249double GetWindMaxPingTime( void )
    250{
    251	int i;
    252	double maxtime = 0;
    253
    254	for( i = 0; i < screenx; i++ )
    255	{
    256		int index = ((current_cycle - i - 1) + PINGCYCLEWIDTH) & (PINGCYCLEWIDTH-1);
    257		double st = PingSendTimes[index];
    258		double rt = PingRecvTimes[index];
    259
    260		double dt = 0;
    261
    262		if( rt > st )
    263		{
    264			dt = rt - st;
    265			dt *= 1000;
    266			if( dt > maxtime ) maxtime = dt;
    267		}
    268	}
    269
    270	return maxtime;
    271}
    272
    273void DrawMainText( const char * stbuf )
    274{
    275	int x, y;
    276	CNFGColor( 0x000000ff );
    277	for( x = -1; x < 2; x++ ) for( y = -1; y < 2; y++ )
    278	{
    279		CNFGPenX = 10+x; CNFGPenY = 10+y;
    280		CNFGDrawText( stbuf, 2 );
    281	}
    282	CNFGColor( 0xffffffff );
    283	CNFGPenX = 10; CNFGPenY = 10;
    284	CNFGDrawText( stbuf, 2 );
    285}
    286
    287void DrawFrameHistogram()
    288{
    289	int i;
    290//	double Now = OGGetAbsoluteTime();
    291	const int colwid = 50;
    292	int categories = (screenx-50)/colwid;
    293	int maxpingslot = ( globmaxtime*10000.0 );
    294	int minpingslot = ( globmintime*10000.0 );
    295	int slots = maxpingslot-minpingslot;
    296
    297	if( categories <= 2 )
    298	{
    299		goto nodata;
    300	}
    301	else
    302	{
    303		int skips = ( (slots)/categories ) + 1;
    304		int slotsmax = maxpingslot / skips + 1;
    305		int slotsmin = minpingslot / skips;
    306		slots = slotsmax - slotsmin;
    307		if( slots <= 0 ) goto nodata;
    308
    309		uint64_t samples[slots+2];
    310		int      ssmsMIN[slots+2];
    311		int      ssmsMAX[slots+2];
    312		int samp = minpingslot - 1;
    313
    314		if( slots <= 1 ) goto nodata;
    315
    316		memset( samples, 0, sizeof( samples ) );
    317		if( samp < 0 ) samp = 0;
    318
    319		uint64_t highestchart = 0;
    320		int tslot = 0;
    321		for( i = slotsmin; i <= slotsmax; i++ )
    322		{
    323			int j;
    324			uint64_t total = 0;
    325			ssmsMIN[tslot] = samp;
    326			for( j = 0; j < skips; j++ )
    327			{
    328				total += hist_counts[samp++];
    329			}
    330
    331			ssmsMAX[tslot] = samp;
    332			if( total > highestchart ) highestchart = total;
    333			samples[tslot++] = total;
    334		}
    335
    336		if( highestchart <= 0 )
    337		{
    338			goto nodata;
    339		}
    340
    341		int rslots = 0;
    342		for( i = 0; i < slots+1; i++ )
    343		{
    344			if( samples[i] ) rslots = i;
    345		}
    346		rslots++;
    347
    348		for( i = 0; i < rslots; i++ )
    349		{
    350			CNFGColor( 0x33cc33ff );
    351			int top = 30;
    352			uint64_t samps = samples[i];
    353			int bottom = screeny - 50;
    354			int height = samps?(samps * (bottom-top) / highestchart + 1):0;
    355			int startx = (i+1) * (screenx-50) / rslots;
    356			int endx = (i+2) * (screenx-50) / rslots;
    357
    358			if( !in_frame_mode )
    359			{
    360				CNFGTackRectangle( startx, bottom-height, endx, bottom + 1 );
    361				CNFGColor( 0x000000ff );
    362			}
    363			else
    364			{
    365				CNFGColor( 0x8080ffff );
    366			}
    367			CNFGTackSegment( startx, bottom+1, endx, bottom+1 );
    368
    369			CNFGTackSegment( startx, bottom-height, startx, bottom+1 );
    370			CNFGTackSegment( endx,   bottom-height, endx,   bottom+1 );
    371
    372			CNFGTackSegment( startx, bottom-height, endx, bottom-height );
    373			char stbuf[1024];
    374			int log10 = 1;
    375			int64_t ll = samps;
    376			while( ll >= 10 )
    377			{
    378				ll /= 10;
    379				log10++;
    380			}
    381
    382			if( !in_frame_mode )
    383			{
    384				CNFGColor( 0xffffffff );
    385			}
    386			else
    387			{
    388				CNFGColor( 0x8080ffff );
    389			}
    390
    391
    392			CNFGPenX = startx + (8-log10) * 4; CNFGPenY = bottom+3;
    393#ifdef WIN32
    394			sprintf( stbuf, "%I64u", samps );
    395#else
    396			sprintf( stbuf, "%lu", samps );
    397#endif
    398			CNFGDrawText( stbuf, 2 );
    399
    400			CNFGPenX = startx; CNFGPenY = bottom+14;
    401			sprintf( stbuf, "%5.1fms\n%5.1fms", ssmsMIN[i]/10.0, ssmsMAX[i]/10.0 );
    402			CNFGDrawText( stbuf, 2 );
    403		}
    404		char stt[1024];
    405#ifdef WIN32
    406		snprintf( stt, 1024, "Host: %s\nHistorical max  %9.2fms\nBiggest interval%9.2fms\nHistorical packet loss %I64u/%I64u = %6.3f%%",
    407#else
    408		snprintf( stt, 1024, "Host: %s\nHistorical max  %9.2fms\nBiggest interval%9.2fms\nHistorical packet loss %lu/%lu = %6.3f%%",
    409#endif
    410			pinghost, globmaxtime*1000.0, globinterval*1000.0, globallost, globalrx, globallost*100.0/(globalrx+globallost) );
    411		if( !in_frame_mode )
    412			DrawMainText( stt );
    413		return;
    414	}
    415nodata:
    416	DrawMainText( "No data.\n" );
    417	return;
    418}
    419
    420
    421void DrawFrame( void )
    422{
    423	int i;
    424
    425	double now = OGGetAbsoluteTime();
    426
    427	double totaltime = 0;
    428	int totalcountok = 0;
    429	int totalcountloss = 0;
    430	double mintime = 10000;
    431	double maxtime = 0;
    432	double stddev = 0;
    433	double last = -1;
    434	double loss = 100.00;
    435	double windmaxtime = GetWindMaxPingTime();
    436
    437	for( i = 0; i < screenx; i++ )
    438	{
    439		int index = ((current_cycle - i - 1) + PINGCYCLEWIDTH) & (PINGCYCLEWIDTH-1);
    440		double st = PingSendTimes[index];
    441		double rt = PingRecvTimes[index];
    442
    443		double dt = 0;
    444
    445		if( rt > st ) // ping received
    446		{
    447			CNFGColor( 0xffffffff );
    448			dt = rt - st;
    449			dt *= 1000;
    450			totaltime += dt;
    451			if( dt < mintime ) mintime = dt;
    452			if( dt > maxtime ) maxtime = dt;
    453			totalcountok++;
    454			if( last < 0)
    455				last = dt;
    456		}
    457		else if (st != 0) // ping sent but not received
    458		{
    459			CNFGColor( 0xff0000ff );
    460			dt = now - st;
    461			dt *= 1000;
    462			if( i > 5 ) totalcountloss++; //Get a freebie on the first 5.
    463		}
    464		else // no ping sent for this point in time (after startup)
    465		{
    466			CNFGColor( 0x000000ff );
    467			dt = 99 * 1000; // assume 99s to fill screen black
    468		}
    469
    470		if (!GuiYscaleFactorIsConstant)
    471		{
    472			GuiYScaleFactor =  (screeny - 50) / windmaxtime;
    473		}
    474
    475		int h = dt*GuiYScaleFactor;
    476		int top = screeny - h;
    477		if( top < 0 ) top = 0;
    478		CNFGTackSegment( i, screeny-1, i, top );
    479	}
    480
    481	double avg = totaltime / totalcountok;
    482	loss = (double) totalcountloss / (totalcountok + totalcountloss) * 100;
    483
    484	for( i = 0; i < screenx; i++ )
    485	{
    486		int index = ((current_cycle - i - 1) + PINGCYCLEWIDTH) & (PINGCYCLEWIDTH-1);
    487		double st = PingSendTimes[index];
    488		double rt = PingRecvTimes[index];
    489
    490		double dt = 0;
    491		if( rt > st )
    492		{
    493			dt = rt - st;
    494			dt *= 1000;
    495			stddev += (dt-avg)*(dt-avg);
    496		}
    497	}
    498
    499	stddev /= totalcountok;
    500
    501	stddev = sqrt(stddev);
    502
    503	int avg_gui    = avg*GuiYScaleFactor;
    504	int stddev_gui = stddev*GuiYScaleFactor;
    505
    506	CNFGColor( 0x00ff00ff );
    507
    508
    509	int l = avg_gui;
    510	CNFGTackSegment( 0, screeny-l, screenx, screeny - l );
    511	l = (avg_gui) + (stddev_gui);
    512	CNFGTackSegment( 0, screeny-l, screenx, screeny - l );
    513	l = (avg_gui) - (stddev_gui);
    514	CNFGTackSegment( 0, screeny-l, screenx, screeny - l );
    515
    516	char stbuf[2048];
    517	char * sptr = &stbuf[0];
    518
    519	sptr += sprintf( sptr, 
    520		"Last:%6.2f ms    Host: %s\n"
    521		"Min :%6.2f ms\n"
    522		"Max :%6.2f ms    Historical max:   %5.2f ms\n"
    523		"Avg :%6.2f ms    Biggest interval: %5.2f ms\n"
    524#ifdef WIN32
    525		"Std :%6.2f ms    Historical loss:  %I64u/%I64u %5.3f%%\n"
    526#else
    527		"Std :%6.2f ms    Historical loss:  %lu/%lu %5.3f%%\n"
    528#endif
    529		"Loss:%6.1f %%", last, pinghost, mintime, maxtime, globmaxtime*1000, avg, globinterval*1000.0, stddev,
    530		globallost, globalrx+globallost, globallost*100.0f/(globalrx+globallost), loss );
    531
    532	DrawMainText( stbuf );
    533	OGUSleep( 1000 );
    534}
    535
    536#ifdef WIN32
    537
    538const char * glargv[10];
    539int glargc = 0;
    540
    541int RegString( int write, char * data, DWORD len )
    542{
    543	HKEY hKey;
    544	if( RegCreateKeyExA(HKEY_CURRENT_USER, "Software\\cnping", 0, NULL,	   
    545		REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL) == ERROR_SUCCESS)
    546	{
    547		if( write )
    548		{
    549			RegSetValueExA( hKey, "history", 0, REG_SZ, (uint8_t*)data, len );
    550			return 0;
    551		}
    552		else
    553		{
    554			DWORD type;
    555			if( RegGetValueA( hKey, "", "history", 0x02, &type, data, &len ) == ERROR_SUCCESS )
    556			{	
    557				return 0;
    558			}
    559			return -16;
    560		}
    561
    562		RegCloseKey( hKey );
    563	}
    564	else
    565	{
    566		return -15;
    567	}
    568}
    569
    570INT_PTR CALLBACK TextEntry( HWND   hwndDlg, UINT   uMsg, WPARAM wParam, LPARAM lParam )
    571{
    572
    573	switch( uMsg )
    574	{
    575	case WM_INITDIALOG:
    576		SetDlgItemText(hwndDlg, 4, "0.02");
    577		SetDlgItemText(hwndDlg, 5, "0" );
    578
    579		char data[1024];
    580		if( !RegString( 0, data, sizeof( data ) ) )
    581		{
    582			SetDlgItemText(hwndDlg, 3, data);
    583		}
    584
    585		return 0;
    586	case WM_COMMAND:
    587		switch( wParam>>24 )
    588		{
    589			case 4: case 3: return 0; //keyboard input
    590			case 1: case 2: return 0; //focus changed.
    591			case 0:
    592			{
    593				int id = wParam & 0xffffff;
    594				if( id == 8 || id == 2 )
    595				{
    596					exit( -1 );
    597				}
    598
    599				char Address[128]; GetDlgItemText(hwndDlg, 3, Address, sizeof(Address));
    600				char Period[128];  GetDlgItemText(hwndDlg, 4, Period, sizeof(Period));
    601				char Extra[128];   GetDlgItemText(hwndDlg, 5, Extra, sizeof(Extra));
    602				char Scaling[128]; GetDlgItemText(hwndDlg, 6, Scaling, sizeof(Scaling));
    603			
    604				if( strlen( Address ) )
    605				{
    606					RegString( 1, Address, strlen( Address ) );
    607
    608					glargc = 2;
    609					glargv[1] = strdup( Address );
    610					if( strlen( Period ) )
    611					{
    612						glargc = 3;
    613						glargv[2] = strdup( Period );
    614						if( strlen( Extra ) )
    615						{
    616							glargc = 4;
    617							glargv[3] = strdup( Extra );
    618							if( strlen( Scaling ) )
    619							{
    620								glargc = 5;
    621								glargv[4] = strdup( Scaling );
    622							}
    623						}
    624					}
    625				}
    626				EndDialog(hwndDlg, 0);
    627				return 0; //User pressed enter.
    628			}
    629		}
    630/*		return 0;
    631	case WM_CTLCOLORBTN:
    632		//printf( "ctr %p %p %p\n", uMsg, wParam, lParam );
    633		//return 0;
    634	case 32: case 512: case 132: case 24: case 70:
    635	case 127: case 783: case 28: case 134: case 6: case 7:
    636	case 8: case 312: case 15: case 71: case 133: case 307:
    637	case 20: case 310: case 33:
    638		return 0;
    639*/
    640	}
    641	return 0;
    642}
    643#endif
    644int main( int argc, const char ** argv )
    645{
    646	char title[1024];
    647	int i;
    648	double ThisTime;
    649	double LastFPSTime = OGGetAbsoluteTime();
    650	double LastFrameTime = OGGetAbsoluteTime();
    651	double SecToWait;
    652	double frameperiodseconds;
    653	const char * device = NULL;
    654
    655#ifdef WIN32
    656	ShowWindow (GetConsoleWindow(), SW_HIDE);
    657#endif
    658
    659	srand( (uintmax_t)(OGGetAbsoluteTime()*100000) );
    660	#ifndef DEBUG
    661	for( i = 0; i < sizeof( pattern ); i++ )
    662	{
    663		pattern[i] = rand();
    664	}
    665	#endif
    666	CNFGBGColor = 0x000080ff;
    667#ifdef WIN32
    668	if( argc < 2 )
    669	{
    670		DialogBox(0, "IPDialog", 0, TextEntry );
    671		argc = glargc;
    672		argv = glargv;
    673	}
    674#endif
    675
    676	pingperiodseconds = 0.02;
    677	ExtraPingSize = 0;
    678	title[0] = 0;
    679	GuiYScaleFactor = 0;
    680
    681	//We need to process all the unmarked parameters.
    682	int argcunmarked = 1;
    683	int displayhelp = 0;
    684
    685	for( i = 1; i < argc; i++ )
    686	{
    687		const char * thisargv = argv[i];
    688		if( thisargv[0] == '-' )
    689		{
    690			int np = ++i;
    691			if( np >= argc )
    692			{
    693				displayhelp = 1;
    694				break;
    695			}
    696			const char * nextargv = argv[np];
    697			//Parameter-based field.
    698			switch( thisargv[1] )
    699			{
    700				case 'h': pinghost = nextargv; break;
    701				case 'p': pingperiodseconds = atof( nextargv ); break;
    702				case 's': ExtraPingSize = atoi( nextargv ); break;
    703				case 'y': GuiYScaleFactor = atof( nextargv ); break;
    704				case 't': sprintf(title, "%s", nextargv); break;
    705				case 'm': in_histogram_mode = 1; break;
    706				case 'I': device = nextargv; break;
    707				default: displayhelp = 1; break;
    708			}
    709		}
    710		else
    711		{
    712			//Unmarked fields
    713			switch( argcunmarked++ )
    714			{
    715				case 1: pinghost = thisargv; break;
    716				case 2: pingperiodseconds = atof( thisargv ); break;
    717				case 3: ExtraPingSize = atoi( thisargv ); break;
    718				case 4: GuiYScaleFactor = atof( thisargv ); break;
    719				case 5: sprintf(title, "%s", thisargv ); break;
    720				default: displayhelp = 1;
    721			}
    722		}
    723	}
    724
    725	if( title[0] == 0 )
    726	{
    727		sprintf( title, "%s - cnping "VERSION, pinghost );
    728	}
    729
    730	if( GuiYScaleFactor > 0 )
    731	{
    732		GuiYscaleFactorIsConstant = 1;
    733	}
    734
    735	if( !pinghost )
    736	{
    737		displayhelp = 1;
    738	}
    739
    740	if( displayhelp )
    741	{
    742		ERRM( "cnping "VERSION" Usage: cnping [host] [period] [extra size] [y-axis scaling] [window title]\n"
    743			"   (-h) [host]                 -- domain, IP address of ping target for ICMP or http host, i.e. http://google.com\n"
    744			"   (-p) [period]               -- period in seconds (optional), default 0.02 \n"
    745			"   (-s) [extra size]           -- ping packet extra size (above 12), optional, default = 0 \n"
    746			"   (-y) [const y-axis scaling] -- use a fixed scaling factor instead of auto scaling (optional)\n"
    747			"   (-t) [window title]         -- the title of the window (optional)\n"
    748			"   (-I) [interface]            -- Sets source interface (i.e. eth0)\n");
    749		return -1;
    750	}
    751
    752#if defined( WIN32 ) || defined( WINDOWS )
    753	if(device)
    754	{
    755		ERRM("Error: Device option is not implemented on your platform. PRs welcome.\n");
    756		exit( -1 );
    757	}
    758	
    759	if( WSAStartup(MAKEWORD(2,2), &wsaData) )
    760	{
    761		ERRM( "Fault in WSAStartup\n" );
    762		exit( -2 );
    763	}
    764	CNFGSetup( title, 320, 155 );
    765#else
    766	CNFGSetupWMClass( title, 320, 155, "cnping", "cnping" );
    767#endif
    768 
    769
    770	if( memcmp( pinghost, "http://", 7 ) == 0 )
    771	{
    772		StartHTTPing( pinghost+7, pingperiodseconds, device );
    773	}
    774	else
    775	{
    776		char* protoEnd = strstr( pinghost, "://" );
    777		if ( protoEnd )
    778		{
    779			int protoSize = protoEnd - pinghost;
    780			char protoBuffer[protoSize + 1];
    781			memcpy( protoBuffer, pinghost, protoSize );
    782			protoBuffer[protoSize] = '\0';
    783			ERRM( "Protocol \"%s\" is not supported\n", protoBuffer );
    784			exit( -1 );
    785		}
    786
    787		ping_setup( pinghost, device );
    788		OGCreateThread( PingSend, 0 );
    789		OGCreateThread( PingListen, 0 );
    790	}
    791
    792
    793	frameperiodseconds = fmin(.2, fmax(.03, pingperiodseconds));
    794
    795	while(1)
    796	{
    797		iframeno++;
    798		CNFGHandleInput();
    799
    800		CNFGClearFrame();
    801		CNFGColor( 0xffffffff );
    802		CNFGGetDimensions( &screenx, &screeny );
    803
    804		if( in_frame_mode )
    805		{
    806			DrawFrame();
    807		}
    808
    809		if( in_histogram_mode )
    810		{
    811			DrawFrameHistogram();
    812		}
    813
    814		CNFGPenX = 100; CNFGPenY = 100;
    815		CNFGColor( 0xff0000ff );
    816		CNFGDrawText( errbuffer, 3 );
    817
    818
    819		frames++;
    820		CNFGSwapBuffers();
    821
    822		ThisTime = OGGetAbsoluteTime();
    823		if( ThisTime > LastFPSTime + 1 )
    824		{
    825			frames = 0;
    826			LastFPSTime+=1;
    827		}
    828
    829		SecToWait = frameperiodseconds - ( ThisTime - LastFrameTime );
    830		LastFrameTime += frameperiodseconds;
    831		//printf("iframeno = %d; SecToWait = %f; pingperiodseconds = %f; frameperiodseconds = %f \n", iframeno, SecToWait, pingperiodseconds, frameperiodseconds);
    832		if( SecToWait > 0 )
    833			OGUSleep( (int)( SecToWait * 1000000 ) );
    834	}
    835
    836	return(0);
    837}
    838