view cc.c @ 84:5d11b526472e

Android: Avoid calling dw_main_sleep() and dw_main_iteration() during expose callbacks. Expose callbacks are the only ones that run on the UI thread... due to Android implementation restrictions. Therefore calling any blocking functions such as dw_mutex_lock() will cause a message loop to be run, via dw_main_sleep() or dw_main_iteration(), these use hacky stuctured exception handling methods to return on Android, so to be safe avoid it completely. Call dw_mutex_trylock() instead, and if it can't lock the mutex... call dw_render_redraw() to trigger a new expose event.
author Brian Smith <brian@dbsoft.org>
date Thu, 16 Dec 2021 09:20:24 -0600
parents 7654af1b1094
children c9449a0e11c5
line wrap: on
line source

#include "dwcompat.h"
#include "dw.h"
#include "cc.h"
#include "stats.h"

/* These variables are used to define each monitor */
TextConfig netavg = { "In (now):    ", "Out (now):    ", 0 };
TextConfig netmax = { "In (max):    ", "Out (max):    ", 0 };
TextConfig nettot = { "Rcvd:   ", "Sent:   ", 0 };
TextConfig memory = { "Memory:    ", "", 0 };
TextConfig uptime = { "Uptime:", "", 0 };
TextConfig tclock = { "12:00AM", "", (void *)1 };
BarConfig drive = { 50, 100};
NetConfig network = { FALSE };
GraphConfig cpu = { FALSE, "" };
unsigned long desks = 4;

/* Enumerate all monitors */
enum display_list {
#if 0
	DISPLAY_DESK,
#endif
	DISPLAY_CPU,
	DISPLAY_NET,
	DISPLAY_NETAVG,
	DISPLAY_NETMAX,
	DISPLAY_NETTOT,
	DISPLAY_MAX
};

/* Define the monitors, these should corespond to the enumeration above */
Instance gList[40]=
{
#if 0
	{"Virtual Desktops"           , desk_draw  , desk_create  , 0,               0, 0, 8, 40, TRUE , &desks },
#endif
	{"CPU Meter"                  , graph_draw , graph_create , cpu_update,      0, 0, 8, 40, TRUE,  &cpu },
	{"Network Meter"              , net_draw   , net_create   , net_update,      0, 0, 8, 30, FALSE, &network },
	{"Network Average"            , text_draw  , text_create  , netavg_update,   0, 0, 8, 30, FALSE, &netavg },
	{"Network Max"                , text_draw  , text_create  , netmax_update,   0, 0, 8, 30, FALSE, &netmax },
	{"Network Total"              , text_draw  , text_create  , nettot_update,   0, 0, 8, 30, FALSE, &nettot },
	{"Memory Meter"               , text_draw  , text_create  , memory_update,   0, 0, 8, 20, FALSE, &memory },
	{"Uptime"                     , text_draw  , text_create  , uptime_update,   0, 0, 8, 20, FALSE, &uptime },
	{"Clock"                      , text_draw  , text_create  , clock_update,    0, 0, 8, 20, FALSE, &tclock },
	{0, 0, 0}
};

char *current_font;
int current_color = 1, current_font_num = 0, minimized = 0;
long x = 0, y = 0;
unsigned long width = 140, height = 600;
HWND in_properties = 0;

/* Array of current drawing colors */
unsigned long current_colors[COLOR_MAX] = {
	DW_RGB(152, 160, 168),
	DW_RGB(128, 128, 128),
	DW_RGB(248, 252, 248),
	DW_RGB(0, 0, 0),
	DW_RGB(200, 204, 200),
	DW_RGB(200, 204, 200),
	DW_RGB(0, 0, 128),
	DW_RGB(200, 204, 200),
	DW_RGB(2, 130, 130),
	DW_RGB(2, 254, 250),
	DW_RGB(250, 254, 2)
};

/* Array to aid in automated configuration saving */
SaveConfig Config[] = 
{
	{ "WIDTH",					TYPE_ULONG,	&width },
	{ "HEIGHT",					TYPE_ULONG,	&height },
	{ "X",						TYPE_INT,	&x },
	{ "Y",						TYPE_INT,	&y },
	{ "FONT",					TYPE_STRING,&current_font },
	{ "COLOR_BACK",			TYPE_ULONG,	&(current_colors[COLOR_BACK]) },
	{ "COLOR_BAR",				TYPE_ULONG,	&(current_colors[COLOR_BAR]) },
	{ "COLOR_HIGH_LIGHT",	TYPE_ULONG,	&(current_colors[COLOR_HIGH_LIGHT]) },
	{ "COLOR_LOW_LIGHT",		TYPE_ULONG,	&(current_colors[COLOR_LOW_LIGHT]) },
	{ "COLOR_BORDER",			TYPE_ULONG,	&(current_colors[COLOR_BORDER]) },
	{ "COLOR_THUMB",			TYPE_ULONG,	&(current_colors[COLOR_THUMB]) },
	{ "COLOR_TEXT",			TYPE_ULONG,	&(current_colors[COLOR_TEXT]) },
	{ "COLOR_AVERAGE",		TYPE_ULONG,	&(current_colors[COLOR_AVERAGE]) },
	{ "COLOR_GRID",			TYPE_ULONG,	&(current_colors[COLOR_GRID]) },
	{ "COLOR_RECV",			TYPE_ULONG,	&(current_colors[COLOR_RECV]) },
	{ "COLOR_SENT",			TYPE_ULONG,	&(current_colors[COLOR_SENT]) },
	{ "", 0, 0}
};

unsigned long Sent = 0, Recv = 0, TotalSent = 0, TotalRecv = 0, MaxSent = 0, MaxRecv = 0;

/* Modify selected flags in the monitor list */
void set_flags(int entry, unsigned long value, unsigned long mask)
{
	unsigned long tmp;

	if(entry > -1)
	{
		tmp = gList[entry].Flags | mask;
		tmp ^= mask;
		tmp |= value;
		gList[entry].Flags = tmp;
	}
}

/* Update the monitor array with the current values */
void update_pos(void)
{
	dw_window_get_pos_size(hwndFrame, &x, &y, &width, &height);

}

/* Set the position of the monitors based on the saved positions in the array */
void restore_pos(void)
{
	dw_window_set_pos_size(hwndFrame, x, y, width, height);
}

/* Write the cc.ini file with all of the current settings */
void saveconfig(void)
{
	char *tmppath = INIDIR, *inidir, *inipath, *home = dw_user_dir(), *inifile = __TARGET__ ".ini";
	int x = 0;
	FILE *f;

	update_pos();

	if(strcmp(INIDIR, ".") == 0)
	{
		inipath = strdup(inifile);
		inidir = strdup(INIDIR);
	}
	else
	{
		/* Need space for the filename, directory separator and NULL */
		int extra = strlen(inifile) + 2;
        
		/* If the path is in the home directory... */
		if(home && tmppath[0] == '~')
		{
			/* Fill in both with the retrieved home directory */
			inipath = calloc(strlen(home) + strlen(INIDIR) + extra, 1);
			inidir = calloc(strlen(home) + strlen(INIDIR) + 1, 1);
			strcpy(inipath, home);
			strcpy(inidir, home);
			/* Append everything after the tilde */
			strcat(inipath, &tmppath[1]);
			strcat(inidir, &tmppath[1]);
		}
		else
		{
			/* Otherwise just copy the entire directory */
			inipath = calloc(strlen(INIDIR) + extra, 1);
			strcat(inipath, INIDIR);
			inidir = strdup(INIDIR);
		}
		/* Add the separator and filename */
		strcat(inipath, DIRSEP);
		strcat(inipath, __TARGET__ ".ini");
	}

	/* Try to open the file for writing */
	f=fopen(inipath, FOPEN_WRITE_TEXT);

	/* If we couldn't open it... */
	if(f==NULL)
	{
		/*  If the ini direcotry isn't the current directory... */
		if(strcmp(INIDIR, ".") != 0)
		{
			/* Try to create the directory */
			makedir(inidir);
			/* Then try to open it again */
			f=fopen(inipath, FOPEN_WRITE_TEXT);
		}
		/* If it still failed... */
		if(f==NULL)
		{
			/* Show an error message */
			dw_messagebox(APP_NAME, DW_MB_ERROR | DW_MB_OK, "Could not save settings. Inipath = \"%s\"", inipath);
			free(inipath);
			free(inidir);
			return;
		}
	}

	/* Free the temporary memory */
	free(inipath);
	free(inidir);

	/* Loop through all saveable settings */
	while(Config[x].type)
	{
		switch(Config[x].type)
		{
			/* Handle saving integers */
			case TYPE_INT:
			{
				int *var = (int *)Config[x].data;

				fprintf(f, "%s=%d\n", Config[x].name, *var);
				break;
			}
			/* Handle saving booleans */
			case TYPE_BOOLEAN:
			{
				int *var = (int *)Config[x].data;

				fprintf(f, "%s=%s\n", Config[x].name, *var ? "TRUE" : "FALSE");
				break;
			}
			/* Handle saving unsigned long integers */
			case TYPE_ULONG:
			{
				unsigned long *var = (unsigned long *)Config[x].data;

				fprintf(f, "%s=%lu\n", Config[x].name, *var);
				break;
			}
			/* Handle saving strings */
			case TYPE_STRING:
			{
				char **str = (char **)Config[x].data;

				fprintf(f, "%s=%s\n", Config[x].name, *str);
				break;
			}
			/* Handle saving floating point */
			case TYPE_FLOAT:
			{
				float *var = (float *)Config[x].data;

				fprintf(f, "%s=%f\n", Config[x].name, *var);
				break;
			}
		}
		x++;
	}

	fclose(f);
}

#define INI_BUFFER 256

/* Generic function to parse information from a config file */
void ini_getline(FILE *f, char *entry, char *entrydata)
{
	/* Allocate zeroed buffer from the stack */
	char in[INI_BUFFER] = { 0 };

	/* Try to read a line into the buffer */
	if(fgets(in, INI_BUFFER - 1, f))
	{
		int len = strlen(in);

		/* Strip off any trailing newlines */
		if(len > 0 && in[len-1] == '\n')
			in[len-1] = 0;

		/* Skip over comment lines starting with # */
		if(in[0] != '#')
		{
			/* Locate = in the line */
			char *equalsign = strchr(in, '=');

			/* If the = was found... */
			if(equalsign)
			{
				/* Replace = with NULL terminator */
				*equalsign = 0;
				/* Copy before the = into entry */
				strcpy(entry, in);
				/* And after the = into entrydata */
				strcpy(entrydata, ++equalsign);
				return;
			}
		}
	}
	/* NULL terminate both variables */
	entrydata[0] = entry[0] = 0;
}

/* Load the cc.ini file from disk setting all the necessary flags */
void loadconfig(void)
{
	char *tmppath = INIDIR, *inipath, *home = dw_user_dir(), *inifile = __TARGET__ ".ini";
	char entry[INI_BUFFER], entrydata[INI_BUFFER];
	FILE *f;

	if(strcmp(INIDIR, ".") == 0)
		inipath = strdup(inifile);
	else
	{
		/* Need space for the filename, directory separator and NULL */
		int extra = strlen(inifile) + 2;

		/* If the path is in the home directory... */
		if(home && tmppath[0] == '~')
		{
			/* Fill it in with the retrieved home directory */
			inipath = calloc(strlen(home) + strlen(INIDIR) + extra, 1);
			strcpy(inipath, home);
			/* Append everything after the tilde */
			strcat(inipath, &tmppath[1]);
		}
		else
		{
			/* Otherwise just copy the entire directory */
			inipath = calloc(strlen(INIDIR) + extra, 1);
			strcpy(inipath, INIDIR);
		}
		/* Add the separator and filename */
		strcat(inipath, DIRSEP);
		strcat(inipath, inifile);
	}

	/* Try to open the file for reading */
	f = fopen(inipath, FOPEN_READ_TEXT);

	/* Free the temporary memory */
	free(inipath);

	/* If we successfully opened the ini file */
	if(f)
	{
		/* Loop through the file */
		while(!feof(f))
		{
			int x = 0;
			
			ini_getline(f, entry, entrydata);

			/* Cycle through the possible settings */
			while(Config[x].type)
			{
				/* If this line has a setting we are looking for */
				if(strcasecmp(entry, Config[x].name)==0)
				{
					switch(Config[x].type)
					{
						/* Load an integer setting */
						case TYPE_INT:
						{
							int *var = (int *)Config[x].data;

							*var = atoi(entrydata);
							break;
						}
						/* Load an boolean setting */
						case TYPE_BOOLEAN:
						{
							int *var = (int *)Config[x].data;

							if(strcasecmp(entrydata, "true")==0)
								*var = TRUE;
							else
								*var = FALSE;
							break;
						}
						/* Load an unsigned long integer setting */
						case TYPE_ULONG:
						{
							unsigned long *var = (unsigned long *)Config[x].data;

							sscanf(entrydata, "%lu", var);
							break;
						}
						/* Load an string setting */
						case TYPE_STRING:
						{
							char **str = (char **)Config[x].data;

							*str = strdup(entrydata);
							break;
						}
						/* Load an floating point setting */
						case TYPE_FLOAT:
						{
							float *var = (float *)Config[x].data;

							*var = atof(entrydata);
							break;
						}
					}
				}
				x++;
			}
		}
		fclose(f);
	}
}

/* Creates pull down or popup menu */
int DWSIGNAL display_menu(HWND hwnd, void *data)
{
	HMENUI hwndMenu;
	HWND menuitem;
	long px, py;

	hwndMenu = dw_menu_new(0L);

	menuitem = dw_menu_append_item(hwndMenu, "Properties", DW_MENU_POPUP, 0L, TRUE, FALSE, DW_NOMENU);
	dw_signal_connect(menuitem, DW_SIGNAL_CLICKED, DW_SIGNAL_FUNC(display_properties), NULL);

	menuitem = dw_menu_append_item(hwndMenu, "~Minimize", DW_MENU_POPUP, 0L, TRUE, FALSE, DW_NOMENU);
	dw_signal_connect(menuitem, DW_SIGNAL_CLICKED, DW_SIGNAL_FUNC(display_minimize), NULL);
	dw_menu_append_item(hwndMenu, "", 0L, 0L, TRUE, FALSE, 0L);
	menuitem = dw_menu_append_item(hwndMenu, "E~xit", DW_MENU_POPUP, 0L, TRUE, FALSE, DW_NOMENU);
	dw_signal_connect(menuitem, DW_SIGNAL_CLICKED, DW_SIGNAL_FUNC(display_exit), NULL);

	dw_pointer_query_pos(&px, &py);
	dw_menu_popup(&hwndMenu, hwndFrame, px, py);
	return TRUE;
}

/* This function creates the display windows */
void display_create(void)
{
	int z = 0;
	ULONG flStyle = DW_FCF_SIZEBORDER | DW_FCF_TASKLIST;

	hwndFrame = dw_window_new(HWND_DESKTOP, "Control Center", flStyle);

	hwndHbox = dw_box_new(DW_VERT, 0);

	dw_box_pack_start(hwndFrame, hwndHbox, 0, 0, TRUE, TRUE, 0);

	while(gList[z].Name)
	{
		if(!(gList[z].Flags & fHidden))
		{
			gList[z].Create(&gList[z], hwndHbox);
		}
		z++;
	}
	hMtx = dw_mutex_new();

	restore_pos();

	dw_window_set_icon(hwndFrame, DW_RESOURCE(MAIN_FRAME));

	dw_window_default(hwndFrame, gList[0].hwndDraw[0]);

	dw_window_show(hwndFrame);

	dw_thread_new((void *)display_update, NULL, 0xFFFF);

}

/* This function continually updates the display windows */
int DWSIGNAL display_update(void)
{
	srand(time(NULL));

	while(display_active)
	{
		int z = 0;

		msleep(1000);

		dw_mutex_lock(hMtx);

		current_time++;

		while(gList[z].Name)
		{
			if(gList[z].Update)
				gList[z].Update(&gList[z], 0);
			if(gList[z].Draw && gList[z].hwndDraw && !(gList[z].Flags & fHidden))
				dw_render_redraw(gList[z].hwndDraw[0]);
			z++;
		}
		dw_mutex_unlock(hMtx);

	}
	display_destroy();
	return FALSE;
}

void display_destroy_monitor(int entry)
{
	int x = 0;

	dw_mutex_lock(hMtx);

	dw_window_destroy(gList[entry].hwnd);
	gList[entry].hwnd = 0;
	if(gList[entry].hwndDraw)
	{
		free(gList[entry].hwndDraw);
		gList[entry].hwndDraw = NULL;
	}
	if(gList[entry].pixmap)
	{
		while(gList[entry].pixmap[x])
		{
			dw_pixmap_destroy(gList[entry].pixmap[x]);
			x++;
		}
		free(gList[entry].pixmap);
		gList[entry].pixmap = NULL;
	}
	dw_mutex_unlock(hMtx);
}

/* This function destroys the display windows */
void display_destroy(void)
{
	int z =0;

	while(gList[z].Name)
	{
		display_destroy_monitor(z);
		z++;
	}
}

/* Returns TRUE if the window handle passed is that of the display ID */
int is_window(HWND hwnd, int which)
{
	if(which < DISPLAY_MAX && which > -1 && gList[which].hwnd == hwnd)
		return TRUE;
	return FALSE;
}

int DWSIGNAL delete_event(HWND hwnd, void *data)
{
	display_destroy();
	exit(0);
	return TRUE;
}

/* Context menus */
int DWSIGNAL display_exit(HWND hwnd, void *data)
{
	dw_mutex_lock(hMtx);
	display_active = FALSE;
	update_pos();
	saveconfig();
	dw_main_quit();
	dw_mutex_unlock(hMtx);
	return TRUE;
}

int DWSIGNAL display_minimize(HWND hwnd, void *data)
{
	dw_window_minimize(hwndFrame);
	return TRUE;
}

/* Called when the color swatch needs to be redrawn */
int DWSIGNAL color_expose(HWND hwnd, DWExpose *exp, void *data)
{
	int color = DW_POINTER_TO_INT(data);
	
	dw_color_foreground_set(current_colors[color]);
	dw_draw_rect(hwnd, 0, DW_DRAW_FILL | DW_DRAW_NOAA, exp->x, exp->y, exp->width, exp->height);
	
	return TRUE;
}

/* Handle changing the color of an item */
int DWSIGNAL color_click(HWND hwnd, int x, int y, int buttonmask, void *data)
{
	int color = DW_POINTER_TO_INT(data);
	unsigned long newcol = dw_color_choose(current_colors[color]);

	if(newcol != current_colors[color])
	{
		current_colors[color] = newcol;

		dw_render_redraw(hwnd);
	}
	return TRUE;
}

/* Handle changing the display font */
int DWSIGNAL font_click(HWND hwnd, void *data)
{
	char *oldfont = current_font;
	char *newfont = dw_font_choose(current_font);
	
	if(newfont)
	{
		int m = 0;
		
		current_font = strdup(newfont);

		/* Update the look and text of the button */
		dw_window_set_font(hwnd, newfont);
		dw_window_set_text(hwnd, newfont);

		/* Update the fonts on the active windows */
		while(gList[m].Name)
		{
			if(gList[m].hwndDraw && gList[m].hwndDraw[0])
			{
				dw_window_set_font(gList[m].hwndDraw[0], newfont);
			}
			m++;
		}

		/* Free the old fonts */
		free(oldfont);
		dw_free(newfont);
	}
	return TRUE;
}

/* Handle properties dialog closing */
int DWSIGNAL properties_delete(HWND hwnd, void *data)
{
	dw_window_destroy(in_properties);
	in_properties = 0;
	return FALSE;
}

/* Create the properties dialog */
int DWSIGNAL display_properties(HWND hwnd, void *data)
{
	HWND notebook, vbox, hbox, tmp;
	ULONG page, flStyle = DW_FCF_TITLEBAR | DW_FCF_SIZEBORDER | DW_FCF_CLOSEBUTTON | DW_FCF_SYSMENU | DW_FCF_TEXTURED;
	int x;

	/* If the window is already open, show it instead */
	if(in_properties)
	{
		dw_window_show(in_properties);
		return TRUE;
	}

	/* Create a new properties dialog */
	in_properties = dw_window_new(DW_DESKTOP, "Properties", flStyle);
	
	notebook = dw_notebook_new(0, TRUE);
	dw_box_pack_start(in_properties, notebook, 0, 0, TRUE, TRUE, 0);

	/* Create the noteboook */
	vbox = dw_scrollbox_new(DW_VERT, 5);
	page = dw_notebook_page_new(notebook, 0, TRUE);
	dw_notebook_pack(notebook, page, vbox);
	dw_notebook_page_set_text(notebook, page, "Appearance");

	/* Current Font */
	hbox = dw_box_new(DW_HORZ, 0);
	dw_box_pack_start(vbox, hbox, 0, 0, FALSE, FALSE, 0);
	tmp = dw_text_new("Display Font:", 0);
	dw_window_set_style(tmp, DW_DT_VCENTER, DW_DT_VCENTER);
	dw_box_pack_start(hbox, tmp, -1, -1, FALSE, TRUE, 2);
	tmp = dw_button_new(current_font, 0);
	dw_window_set_font(tmp, current_font);
	dw_signal_connect(tmp, DW_SIGNAL_CLICKED, DW_SIGNAL_FUNC(font_click), NULL);
	dw_box_pack_start(hbox, tmp, -1, -1, FALSE, FALSE, 2);

	/* Create displays for all the colors */
	for(x=0;x<COLOR_MAX;x++)
	{
		hbox = dw_box_new(DW_HORZ, 0);
		dw_box_pack_start(vbox, hbox, 0, 0, TRUE, FALSE, 0);
		tmp = dw_render_new(0);
		dw_window_set_pointer(tmp, DW_POINTER_ARROW);
		dw_signal_connect(tmp, DW_SIGNAL_EXPOSE, DW_SIGNAL_FUNC(color_expose), DW_INT_TO_POINTER(x));
		dw_signal_connect(tmp, DW_SIGNAL_BUTTON_PRESS, DW_SIGNAL_FUNC(color_click), DW_INT_TO_POINTER(x));
		dw_box_pack_start(hbox, tmp, 40, 25, FALSE, FALSE, 2);
		tmp = dw_text_new(color_names[x], 0);
		dw_window_set_style(tmp, DW_DT_VCENTER, DW_DT_VCENTER);
		dw_box_pack_start(hbox, tmp, -1, -1, TRUE, TRUE, 2);
	}

	dw_signal_connect(in_properties, DW_SIGNAL_DELETE, DW_SIGNAL_FUNC(properties_delete), NULL);

	dw_window_set_size(in_properties, 300, 400);
	dw_window_show(in_properties);
	return TRUE;
}

/* This gets called when the graph resizes */
int DWSIGNAL display_configure(HWND hwnd, int width, int height, void *data)
{
	Instance *inst = (Instance *)data;

	if(inst)
	{
		int z = 0;

		while(inst->hwndDraw[z])
		{
			if(hwnd == inst->hwndDraw[z])
			{
				dw_mutex_lock(hMtx);

				if(inst->pixmap[z])
					dw_pixmap_destroy(inst->pixmap[z]);

				inst->pixmap[z] = dw_pixmap_new(inst->hwndDraw[z], width, height, 0);

				dw_mutex_unlock(hMtx);

				dw_render_redraw(hwnd);

				break;
			}
			z++;
		}
	}
	return TRUE;
}

/* This gets called when a part of the graph needs to be repainted. */
int DWSIGNAL display_expose(HWND hwnd, DWExpose *exp, void *data)
{
	Instance *inst = (Instance *)data;

	if(inst)
	{
		if(dw_mutex_trylock(hMtx) == DW_ERROR_NONE)
		{
			inst->Draw(inst);

			dw_mutex_unlock(hMtx);
		}
		else
			dw_render_redraw(hwnd);
	}
	return TRUE;
}

/* Handles button presses on the graph window */
int DWSIGNAL display_button_press(HWND hwnd, int x, int y, int button, void *data)
{
	/* When clicked CC should take focus, but
	 * do it before creating the menu.
	 */
	dw_window_show(hwndFrame);
	if(button == 1)
	{
		dragx = x;
		dragy = y;
		button_down = 1;
		dw_window_capture(hwnd);
	}
	else if (button == 2)
		display_menu(hwnd, 0);
	return TRUE;
}

/* Handles button releases on the graph window */
int DWSIGNAL display_button_release(HWND hwnd, int x, int y, int button, void *data)
{
	if(button == 1)
	{
		button_down = 0;
		dw_window_release();
	}
	return TRUE;
}

/* Handles motion events to move the graph window when button 1 is down */
int DWSIGNAL display_motion_notify(HWND hwnd, int x, int y, int buttonmask, void *data)
{
	Instance *inst = (Instance *)data;
	static int lastx = 0, lasty = 0;

	if(lastx == x && lasty == y)
		return TRUE;

	if(inst && button_down == 1 && (buttonmask & DW_BUTTON1_MASK))
	{
		int delta_x = x - dragx, delta_y = y - dragy;
		long graphx, graphy;

		dw_window_get_pos_size(hwndFrame, &graphx, &graphy, NULL, NULL);
		dw_window_set_pos(hwndFrame, graphx + delta_x, graphy + delta_y);

		lastx = x;
		lasty = y;
	}
	return TRUE;
}

void draw_box(HPIXMAP hPixmap, int x, int y, int width, int height, int indent, unsigned long left, unsigned long top, unsigned long right, unsigned long bottom, int fill, unsigned long fillcolor)
{
	if(indent * 2 > width)
		return;

	dw_color_foreground_set(left);
	dw_draw_line(0, hPixmap, x + indent, y + indent, x + indent, height - indent - 1);
	dw_color_foreground_set(top);
	dw_draw_line(0, hPixmap, x + indent, y + indent, width - indent - 1, y + indent);
	dw_color_foreground_set(right);
	dw_draw_line(0, hPixmap, width - indent - 1, y + indent + 1, width - indent - 1, height - indent - 1);
	dw_color_foreground_set(bottom);
	dw_draw_line(0, hPixmap, x + indent, height - indent - 1, width - indent - 2, height - indent - 1);
	if(fill && ((indent + 1) * 2) < width)
	{
		dw_color_foreground_set(fillcolor);
		dw_draw_rect(0, hPixmap, DW_DRAW_FILL | DW_DRAW_NOAA, x + indent + 1, y + indent + 1, width - ((indent + 1) * 2), height - ((indent + 1) * 2));
	}
}

void graph_add_statistic(int id, unsigned long value)
{
	if(id > -1 && id < DISPLAY_MAX)
	{
		unsigned long *values = (unsigned long *)gList[id].internal;

		/* Advance the items */
		memmove(&values[1], values, (16384/GRID_STEP));

		values[0] = value;
	}
}

void net_add_statistic(int id, unsigned long value1, unsigned long value2)
{
	if(id > -1 && id < DISPLAY_MAX)
	{
		unsigned long *values = (unsigned long *)gList[id].internal;

		/* Advance the items */
		memmove(&values[1], values, (16384/GRID_STEP) - 1);

		values[0] = value2;

		values = &values[(16384/GRID_STEP)];

		/* Advance the items */
		memmove(&values[1], values, (16384/GRID_STEP) - 1);

		values[0] = value1;
	}
}

unsigned long graph_find_max(struct _instance *inst, int count)
{
	unsigned long max = 0, *values = (unsigned long *)inst->internal;
	int z;

	for(z=0;z<count;z++)
	{
		if(values[z] > max)
			max = values[z];
	}
	return max;
}

unsigned long graph_find_average(struct _instance *inst, int count)
{
	unsigned long max = 0, *values = (unsigned long *)inst->internal;
	double total = 0;
	int z;

	for(z=0;z<count;z++)
	{
		if(values[z] > max)
			total += values[z];
	}
	return (unsigned long)total/count;
}

/* Creates a graph style display */
void graph_create(struct _instance *inst, HWND owner)
{
	inst->hwndDraw = calloc(2, sizeof(HWND));
	inst->pixmap = calloc(2, sizeof(HPIXMAP));

	inst->internal = calloc((16384/GRID_STEP) + 1, sizeof(unsigned long));

	inst->hwndDraw[0] = dw_render_new(125);
	dw_window_set_font(inst->hwndDraw[0], current_font);

	dw_box_pack_start(owner, inst->hwndDraw[0], inst->width, inst->height, TRUE, inst->vsize, 0);

	dw_signal_connect(inst->hwndDraw[0], DW_SIGNAL_CONFIGURE, DW_SIGNAL_FUNC(display_configure), inst);
	dw_signal_connect(inst->hwndDraw[0], DW_SIGNAL_EXPOSE, DW_SIGNAL_FUNC(display_expose), inst);
	dw_signal_connect(inst->hwndDraw[0], DW_SIGNAL_BUTTON_PRESS, DW_SIGNAL_FUNC(display_button_press), inst);
	dw_signal_connect(inst->hwndDraw[0], DW_SIGNAL_BUTTON_RELEASE, DW_SIGNAL_FUNC(display_button_release), inst);
	dw_signal_connect(inst->hwndDraw[0], DW_SIGNAL_MOTION_NOTIFY, DW_SIGNAL_FUNC(display_motion_notify), inst);
}

/* Draw a graph style display */
void graph_draw(struct _instance *inst)
{
	unsigned long width, height;
	int z;
	GraphConfig *graphconfig = (GraphConfig *)inst->custom;

	if(inst && inst->pixmap && *(inst->pixmap) && graphconfig)
	{
		HPIXMAP hPixmap;
		unsigned long *values = (unsigned long *)inst->internal;

		hPixmap = *(inst->pixmap);

		width = DW_PIXMAP_WIDTH(hPixmap);
		height = DW_PIXMAP_HEIGHT(hPixmap);

		dw_color_foreground_set(current_colors[COLOR_BACK]);

		/* Clear the graph area */
		dw_draw_rect(0, hPixmap, DW_DRAW_FILL | DW_DRAW_NOAA, LEFT_SIDE, TOP_BOTTOM, width - (LEFT_SIDE + RIGHT_SIDE), height - (TOP_BOTTOM * 2));

		dw_color_foreground_set(current_colors[COLOR_BAR]);

		if(width > (RIGHT_SIDE + LEFT_SIDE))
		{
			int item = 0, myheight = height - (TOP_BOTTOM * 2);
			unsigned long max, average;

			/* Draw vertical lines */
			for(z=((4)-(current_time%4))*GRID_STEP;z<(width - (RIGHT_SIDE + LEFT_SIDE));z+=(GRID_STEP*4))
				dw_draw_line(0, hPixmap, z + LEFT_SIDE, TOP_BOTTOM, z + LEFT_SIDE, height - TOP_BOTTOM - 1);

			/* Draw horizontal lines */
			for(z=0;z<height;z+=(GRID_STEP*3))
				dw_draw_line(0, hPixmap, LEFT_SIDE, z + TOP_BOTTOM, width - RIGHT_SIDE - 1, z + TOP_BOTTOM);

			max = 100; /*graph_find_max(inst, ((width - (RIGHT_SIDE - LEFT_SIDE))/GRID_STEP) + 1);*/

			if(graphconfig->average)
				average = graph_find_average(inst, ((width - (RIGHT_SIDE - LEFT_SIDE))/GRID_STEP) + 1);

			if(max < 10)
				max = 10;

			/* Draw the Average */
			if(graphconfig->average)
			{
				dw_color_foreground_set(current_colors[COLOR_AVERAGE]);

				dw_draw_line(0, hPixmap, LEFT_SIDE, (myheight + TOP_BOTTOM) - (int)((float)myheight * ((float)average/(float)max)), width - RIGHT_SIDE, (myheight + TOP_BOTTOM) - (int)((float)myheight * ((float)average/(float)max)));
			}

			/* Draw the actual graph values. */
			dw_color_foreground_set(current_colors[COLOR_TEXT]);

			for(z=(width - (RIGHT_SIDE - LEFT_SIDE));z>LEFT_SIDE;z-=GRID_STEP)
			{
				dw_draw_line(0, hPixmap, z, (myheight + TOP_BOTTOM) - (int)((float)myheight * ((float)values[item]/(float)max)), z - GRID_STEP, (myheight + TOP_BOTTOM) - (int)((float)myheight * ((float)values[item+1]/(float)max)));
				item++;
			}

		}

		/* Draw some labels */
		dw_color_foreground_set(DW_RGB(190,190,190));
		dw_draw_text(0, hPixmap, 2 + LEFT_SIDE, 3 + TOP_BOTTOM, graphconfig->text);

		dw_color_foreground_set(current_colors[COLOR_TEXT]);
		dw_draw_text(0, hPixmap, 1 + LEFT_SIDE, 2 + TOP_BOTTOM, graphconfig->text);

		if(graphconfig->average)
		{
			dw_color_foreground_set(current_colors[COLOR_AVERAGE]);

			dw_draw_text(0, hPixmap, 1 + LEFT_SIDE, 20 + TOP_BOTTOM, "Average");
		}

		/* Draw window border */
		dw_color_foreground_set(current_colors[COLOR_BORDER]);
		dw_draw_line(0, hPixmap, 0, 0, width, 0);
		dw_draw_line(0, hPixmap, 0, 0, 0, height);
		dw_draw_line(0, hPixmap, 0, height - 1, width, height - 1);
		dw_draw_line(0, hPixmap, width - 1, 0, width - 1, height);

		dw_color_foreground_set(current_colors[COLOR_LOW_LIGHT]);
		dw_draw_line(0, hPixmap, WINDOW_BORDER + 1, WINDOW_BORDER + 1, WINDOW_BORDER + 1, height - (WINDOW_BORDER + 1));
		dw_draw_line(0, hPixmap, WINDOW_BORDER + 1, WINDOW_BORDER + 1, width - RIGHT_SIDE, WINDOW_BORDER + 1);

		/* Draw thumb border */
		dw_color_foreground_set(current_colors[COLOR_THUMB]);
		dw_draw_line(0, hPixmap, WINDOW_BORDER, WINDOW_BORDER, width - 1, WINDOW_BORDER);
		dw_draw_line(0, hPixmap, WINDOW_BORDER, WINDOW_BORDER, WINDOW_BORDER, height - 1);
		dw_draw_line(0, hPixmap, WINDOW_BORDER, height - 1, width - 1, height - 1);

		/* Draw thumb */
		dw_draw_rect(0, hPixmap, DW_DRAW_FILL | DW_DRAW_NOAA, width - RIGHT_SIDE + 1, 1, THUMB_WIDTH + 1, height - 2);

		/* Draw last of the graph's border */
		dw_color_foreground_set(current_colors[COLOR_HIGH_LIGHT]);
		dw_draw_line(0, hPixmap, width - RIGHT_SIDE, WINDOW_BORDER + 1, width - RIGHT_SIDE, height - TOP_BOTTOM);
		dw_draw_line(0, hPixmap, WINDOW_BORDER + 1, height - TOP_BOTTOM, width - RIGHT_SIDE, height - TOP_BOTTOM);

		/* Blit the image from the memory pixmap to the screen window */
		dw_pixmap_bitblt(*(inst->hwndDraw), 0, 0, 0, width, height, 0, hPixmap, 0, 0);

		/* Make sure everything drawn is visible. */
		dw_flush();
	}

}

/* Creates a stats style display */
void text_create(struct _instance *inst, HWND owner)
{
	inst->hwndDraw = calloc(2, sizeof(HWND));
	inst->pixmap = calloc(2, sizeof(HPIXMAP));

	inst->hwndDraw[0] = dw_render_new(125);
	dw_window_set_font(inst->hwndDraw[0], current_font);

	dw_box_pack_start(owner, inst->hwndDraw[0], inst->width, inst->height, TRUE, inst->vsize, 0);

	dw_signal_connect(inst->hwndDraw[0], DW_SIGNAL_CONFIGURE, DW_SIGNAL_FUNC(display_configure), inst);
	dw_signal_connect(inst->hwndDraw[0], DW_SIGNAL_EXPOSE, DW_SIGNAL_FUNC(display_expose), inst);
	dw_signal_connect(inst->hwndDraw[0], DW_SIGNAL_BUTTON_PRESS, DW_SIGNAL_FUNC(display_button_press), inst);
	dw_signal_connect(inst->hwndDraw[0], DW_SIGNAL_BUTTON_RELEASE, DW_SIGNAL_FUNC(display_button_release), inst);
	dw_signal_connect(inst->hwndDraw[0], DW_SIGNAL_MOTION_NOTIFY, DW_SIGNAL_FUNC(display_motion_notify), inst);
}

/* Draw a stats style display */
void text_draw(struct _instance *inst)
{
	unsigned long width, height;

	if(inst && inst->pixmap && *(inst->pixmap))
	{
		TextConfig *texts = (TextConfig *)inst->custom;
		HPIXMAP hPixmap;

		hPixmap = inst->pixmap[0];

		if(hPixmap)
		{
			int start1 = 3, start2 = 3, textwidth;

			width = DW_PIXMAP_WIDTH(hPixmap);
			height = DW_PIXMAP_HEIGHT(hPixmap);

			/* Clear the graph area */
			dw_color_foreground_set(current_colors[COLOR_THUMB]);
			dw_draw_rect(0, hPixmap, DW_DRAW_FILL | DW_DRAW_NOAA, 0, 0, width, height);

			draw_box(hPixmap, 0, 0, width, height, 0, DW_RGB(0,0,0), DW_RGB(0,0,0), DW_RGB(255,255,255), DW_RGB(255,255,255), 0, 0);

			if(texts->user)
			{
				dw_font_text_extents_get(0, hPixmap, texts->text1, &textwidth, 0);
				start1 = ((width - textwidth) / 2);
				if(texts->text2[0])
				{
					dw_font_text_extents_get(0, hPixmap, texts->text2, &textwidth, 0);
					start2 = ((width - textwidth) / 2);
				}
			}

			/* Draw some labels */
			dw_color_foreground_set(DW_RGB(255,255,255));
			if(texts->text2[0])
			{
				dw_draw_text(0, hPixmap, start1 + 1 + LEFT_SIDE, ((((height - (TOP_BOTTOM * 2))/2) - 12)/2) + 1, texts->text1);
				dw_draw_text(0, hPixmap, start2 + 1 + LEFT_SIDE, ((height - (TOP_BOTTOM * 2))/2) + ((((height - (TOP_BOTTOM * 2))/2)-12)/2) + 1, texts->text2);
			}
			else
				dw_draw_text(0, hPixmap, start1 + 1 + LEFT_SIDE, TOP_BOTTOM + 1, texts->text1);


			dw_color_foreground_set(DW_RGB(0,0,0));
			if(texts->text2[0])
			{
				dw_draw_text(0, hPixmap, start1 + LEFT_SIDE, ((((height - (TOP_BOTTOM * 2))/2) - 12)/2), texts->text1);
				dw_draw_text(0, hPixmap, start2 + LEFT_SIDE, ((height - (TOP_BOTTOM * 2))/2) + ((((height - (TOP_BOTTOM * 2))/2)-12)/2), texts->text2);
			}
			else
				dw_draw_text(0, hPixmap, start1 + LEFT_SIDE, TOP_BOTTOM, texts->text1);

			/* Blit the image from the memory pixmap to the screen window */
			dw_pixmap_bitblt(inst->hwndDraw[0], 0, 0, 0, width, height, 0, hPixmap, 0, 0);

			/* Make sure everything drawn is visible. */
			dw_flush();
		}
	}

}

/* Draw a 1 bar style display */
void bar_draw(struct _instance *inst)
{
	unsigned long width, height;
	BarConfig *barconfig = (BarConfig *)inst->custom;

	if(inst && inst->pixmap && *(inst->pixmap))
	{
		HPIXMAP hPixmap;
		float ratio = ((float)barconfig->currentval)/((float)barconfig->maximum);
		int barwidth;
		unsigned long color;

		hPixmap = *(inst->pixmap);

		width = DW_PIXMAP_WIDTH(hPixmap);
		height = DW_PIXMAP_HEIGHT(hPixmap);

		barwidth = (int)(ratio * ((float)width));

		dw_color_foreground_set(current_colors[COLOR_THUMB]);
		dw_draw_rect(0, hPixmap, DW_DRAW_FILL | DW_DRAW_NOAA, 0, 0, width, height);

		draw_box(hPixmap, 0, 0, width, height, 0, DW_RGB(0,0,0), DW_RGB(0,0,0), DW_RGB(255,255,255), DW_RGB(255,255,255), 0, 0);

		if(ratio > 0.9)
			color = DW_RGB(255, 60, 60);
		else if(ratio > 0.8)
		{
			int val = ((0.9 - ratio) * 10) * 195;
			color = DW_RGB(255, 60 + val, 60);
		}
		else if(ratio > 0.7)
		{
			int val = ((0.8 - ratio) * 10) * 195;
			color = DW_RGB(255 - val, 255, 60);
		}
		else
			color = DW_RGB(60, 255, 60);

		draw_box(hPixmap, 0, 0, barwidth, height, 1, DW_RGB(255,255,255), DW_RGB(255,255,255), DW_RGB(0,0,0), DW_RGB(0,0,0), 1, color);

		dw_color_foreground_set(DW_RGB(190,190,190));
		dw_draw_text(0, hPixmap, 4 + LEFT_SIDE, TOP_BOTTOM + 1, barconfig->text);
		dw_color_foreground_set(DW_RGB(0,0,0));
		dw_draw_text(0, hPixmap, 3 + LEFT_SIDE, TOP_BOTTOM, barconfig->text);

		/* Blit the image from the memory pixmap to the screen window */
		dw_pixmap_bitblt(*(inst->hwndDraw), 0, 0, 0, width, height, 0, hPixmap, 0, 0);

		/* Make sure everything drawn is visible. */
		dw_flush();
	}

}

/* Creates a 1 bar style display */
void bar_create(struct _instance *inst, HWND owner)
{
	inst->hwndDraw = calloc(2, sizeof(HWND));
	inst->pixmap = calloc(2, sizeof(HPIXMAP));

	inst->hwndDraw[0] = dw_render_new(125);
	dw_window_set_font(inst->hwndDraw[0], current_font);

	dw_box_pack_start(owner, inst->hwndDraw[0], inst->width, inst->height, TRUE, inst->vsize, 0);

	dw_signal_connect(inst->hwndDraw[0], DW_SIGNAL_CONFIGURE, DW_SIGNAL_FUNC(display_configure), inst);
	dw_signal_connect(inst->hwndDraw[0], DW_SIGNAL_EXPOSE, DW_SIGNAL_FUNC(display_expose), inst);
	dw_signal_connect(inst->hwndDraw[0], DW_SIGNAL_BUTTON_PRESS, DW_SIGNAL_FUNC(display_button_press), inst);
	dw_signal_connect(inst->hwndDraw[0], DW_SIGNAL_BUTTON_RELEASE, DW_SIGNAL_FUNC(display_button_release), inst);
	dw_signal_connect(inst->hwndDraw[0], DW_SIGNAL_MOTION_NOTIFY, DW_SIGNAL_FUNC(display_motion_notify), inst);
}

/* Creates the dock window */
void desk_create(struct _instance *inst, HWND owner)
{
	unsigned long *desks = (unsigned long *)inst->custom;
	int z;

	inst->hwndDraw = calloc(*desks + 1, sizeof(HWND));
	inst->pixmap = calloc(*desks + 1, sizeof(HPIXMAP));


	for(z=0;z<*desks;z++)
	{
		inst->hwndDraw[z] = dw_render_new(125+z);
		dw_window_set_font(inst->hwndDraw[z], current_font);

		dw_box_pack_start(owner, inst->hwndDraw[z], inst->width, inst->height, TRUE, inst->vsize, 0);

		dw_signal_connect(inst->hwndDraw[z], DW_SIGNAL_CONFIGURE, DW_SIGNAL_FUNC(display_configure), inst);
		dw_signal_connect(inst->hwndDraw[z], DW_SIGNAL_EXPOSE, DW_SIGNAL_FUNC(display_expose), inst);
		dw_signal_connect(inst->hwndDraw[z], DW_SIGNAL_BUTTON_PRESS, DW_SIGNAL_FUNC(display_button_press), inst);
		dw_signal_connect(inst->hwndDraw[z], DW_SIGNAL_BUTTON_RELEASE, DW_SIGNAL_FUNC(display_button_release), inst);
		dw_signal_connect(inst->hwndDraw[z], DW_SIGNAL_MOTION_NOTIFY, DW_SIGNAL_FUNC(display_motion_notify), inst);
	}
}

/* Draw a log style display */
void desk_draw(struct _instance *inst)
{
	unsigned long width, height, z;

	if(inst && inst->pixmap)
	{
		unsigned long *desks = (unsigned long *)inst->custom;

		for(z=0;z<*desks;z++)
		{
			HPIXMAP hPixmap;

			hPixmap = inst->pixmap[z];

			width = DW_PIXMAP_WIDTH(hPixmap);
			height = DW_PIXMAP_HEIGHT(hPixmap);

			/* Draw window border */
			dw_color_foreground_set(DW_RGB(0,0,0));
			dw_draw_rect(0, hPixmap, DW_DRAW_FILL | DW_DRAW_NOAA, 0, 0, width, height);

			draw_box(hPixmap, 0, 0, width, height, 0, DW_RGB(100,100,100), DW_RGB(100,100,100), DW_RGB(255,255,255), DW_RGB(255,255,255), 0, 0);

			/* Blit the image from the memory pixmap to the screen window */
			dw_pixmap_bitblt(inst->hwndDraw[z], 0, 0, 0, width, height, 0, hPixmap, 0, 0);

			/* Make sure everything drawn is visible. */
			dw_flush();
		}
	}

}

/* Creates the dock window */
void net_create(struct _instance *inst, HWND owner)
{
	inst->hwndDraw = calloc(2, sizeof(HWND));
	inst->pixmap = calloc(2, sizeof(HPIXMAP));

	inst->internal = calloc(((16384/GRID_STEP) * 2) + 1, sizeof(unsigned long));

	inst->hwndDraw[0] = dw_render_new(125);
	dw_window_set_font(inst->hwndDraw[0], current_font);

	dw_box_pack_start(owner, inst->hwndDraw[0], inst->width, inst->height, TRUE, inst->vsize, 0);

	dw_signal_connect(inst->hwndDraw[0], DW_SIGNAL_CONFIGURE, DW_SIGNAL_FUNC(display_configure), inst);
	dw_signal_connect(inst->hwndDraw[0], DW_SIGNAL_EXPOSE, DW_SIGNAL_FUNC(display_expose), inst);
	dw_signal_connect(inst->hwndDraw[0], DW_SIGNAL_BUTTON_PRESS, DW_SIGNAL_FUNC(display_button_press), inst);
	dw_signal_connect(inst->hwndDraw[0], DW_SIGNAL_BUTTON_RELEASE, DW_SIGNAL_FUNC(display_button_release), inst);
	dw_signal_connect(inst->hwndDraw[0], DW_SIGNAL_MOTION_NOTIFY, DW_SIGNAL_FUNC(display_motion_notify), inst);
}

int net_find_max(unsigned long *values, int width)
{
	int z, item = 0, max = 1000;

	for(z=0;z<width;z+=GRID_STEP)
	{
		if(values[item] > max)
			max = (int)values[item];
		item++;
	}
	return max;
}

/* Draw a log style display */
void net_draw(struct _instance *inst)
{
	unsigned long width, height, z;

	if(inst && inst->pixmap)
	{
		HPIXMAP hPixmap;
		unsigned long *values = (unsigned long *)inst->internal;

		hPixmap = *(inst->pixmap);

		width = DW_PIXMAP_WIDTH(hPixmap);
		height = DW_PIXMAP_HEIGHT(hPixmap);

		/* Draw window border */
		dw_color_foreground_set(current_colors[COLOR_THUMB]);
		dw_draw_rect(0, hPixmap, DW_DRAW_FILL | DW_DRAW_NOAA, 0, 0, width, height);
		dw_color_foreground_set(DW_RGB(0,0,0));
		dw_draw_rect(0, hPixmap, DW_DRAW_FILL | DW_DRAW_NOAA, 2, 2, width - 4, height - 4);

		draw_box(hPixmap, 0, 0, width, height, 2, DW_RGB(100,100,100), DW_RGB(100,100,100), DW_RGB(255,255,255), DW_RGB(255,255,255), 0, 0);

		dw_color_foreground_set(current_colors[COLOR_GRID]);

		if(width > (RIGHT_SIDE + LEFT_SIDE))
		{
			int item = 0, myheight = (height - 6)/2, mywidth = width - 6;
			unsigned long max;

			/* Draw vertical lines */
			for(z=((4)-(current_time%4))*GRID_STEP;z<(width - (RIGHT_SIDE + LEFT_SIDE));z+=(GRID_STEP*4))
				dw_draw_line(0, hPixmap, z + LEFT_SIDE, TOP_BOTTOM + 1, z + LEFT_SIDE, height - TOP_BOTTOM - 2);

			/* Draw horizontal lines */
			for(z=0;z<(height-1);z+=(GRID_STEP*3))
				dw_draw_line(0, hPixmap, LEFT_SIDE, z + TOP_BOTTOM + 1, width - RIGHT_SIDE - 2, z + TOP_BOTTOM + 1);

			/* Draw the actual graph values. */
			max = net_find_max(values, mywidth);

			if(max)
			{
				dw_color_foreground_set(current_colors[COLOR_RECV]);

				for(z=(width - 4);z>2;z-=GRID_STEP)
				{
					int point = (int)((float)myheight * ((float)values[item]/(float)max));
					if(point)
						dw_draw_rect(0, hPixmap, DW_DRAW_FILL | DW_DRAW_NOAA, z,  (myheight - point) + 3, GRID_STEP, point);
					item++;
				}
			}

			/* Update the variables to the new section. */
			values = &values[(16384/GRID_STEP)];
			item = 0;
			max = net_find_max(values, mywidth);

			if(max)
			{

				dw_color_foreground_set(current_colors[COLOR_SENT]);

				for(z=(width - 4);z>2;z-=GRID_STEP)
				{
					int point = (int)((float)myheight * ((float)values[item]/(float)max));
					if(point)
						dw_draw_rect(0, hPixmap, DW_DRAW_FILL | DW_DRAW_NOAA, z,  myheight + 3, GRID_STEP, point);
					item++;
				}
			}
		}

		/* Blit the image from the memory pixmap to the screen window */
		dw_pixmap_bitblt(*(inst->hwndDraw), 0, 0, 0, width, height, 0, hPixmap, 0, 0);

		/* Make sure everything drawn is visible. */
		dw_flush();
	}

}

void ScaledPrint(char *cBuff, long double value, int decimals)
{
	char fstr[] = "%.0Lf ", *dec = &fstr[2], *not = &fstr[5];
	long double real_value = value;
	static long double giga = 1073741824.0;

	*dec = decimals + '0';

	if(value >= (giga*1024*1024))
	{
		real_value = (long double)value/(giga*1024.0*1024.0);
		*not = 'P';
	}
	else if(value >= (giga*1024))
	{
		real_value = (long double)value/(giga*1024.0);
		*not = 'T';
	}
	else if(value >= giga)
	{
		real_value = (long double)value/(long double)giga;
		*not = 'G';
	}
	else if(value >= (1024*1024))
	{
		real_value = (long double)value/(1024.0*1024.0);
		*not = 'M';
	}
	else if(value >= 1024)
	{
		real_value = (long double)value/1024.0;
		*not = 'K';
	}
	else
		*dec = '0';

	sprintf(cBuff, fstr, (long double)real_value);
}

void cpu_update(struct _instance *inst, HWND owner)
{
	if(inst && inst->pixmap && *(inst->pixmap))
	{
		GraphConfig *graph = (GraphConfig *)inst->custom;
		double Load;
		unsigned long height;

		dw_window_get_pos_size(gList[DISPLAY_CPU].hwndDraw[0], 0, 0, 0, &height);

		Get_Load(&Load);

		sprintf(graph->text, "%d%%", (int)(Load*100));

		graph_add_statistic(DISPLAY_CPU, (unsigned long)(Load*100));
	}
}

void net_update(struct _instance *inst, HWND owner)
{
	Get_Net(&Sent, &Recv, &TotalSent, &TotalRecv);
	net_add_statistic(DISPLAY_NET, Sent, Recv);
}

void netavg_update(struct _instance *inst, HWND owner)
{
	if(inst && inst->pixmap && *(inst->pixmap))
	{
		TextConfig *texts = (TextConfig *)inst->custom;
		ScaledPrint(&texts->text1[12], Recv, 2);
		ScaledPrint(&texts->text2[12], Sent, 2);
	};
}

void netmax_update(struct _instance *inst, HWND owner)
{
	if(Sent > MaxSent)
		MaxSent = Sent;
	if(Recv > MaxRecv)
		MaxRecv = Recv;

	if(inst && inst->pixmap && *(inst->pixmap))
	{
		TextConfig *texts = (TextConfig *)inst->custom;
		ScaledPrint(&texts->text1[12], MaxRecv, 2);
		ScaledPrint(&texts->text2[12], MaxSent, 2);
	}
}

void nettot_update(struct _instance *inst, HWND owner)
{
	if(inst && inst->pixmap && *(inst->pixmap))
	{
		TextConfig *texts = (TextConfig *)inst->custom;
 		ScaledPrint(&texts->text1[7], TotalRecv, 2);
		ScaledPrint(&texts->text2[7], TotalSent, 2);
	}
}

void memory_update(struct _instance *inst, HWND owner)
{
	if(inst && inst->pixmap && *(inst->pixmap))
	{
		TextConfig *texts = (TextConfig *)inst->custom;
		long double Memory;

		Get_Memory(&Memory);
		ScaledPrint(&texts->text1[9], Memory, 2);
	}
}

void uptime_update(struct _instance *inst, HWND owner)
{
	if(inst && inst->pixmap && *(inst->pixmap))
	{
		TextConfig *texts = (TextConfig *)inst->custom;
		unsigned long Seconds;

		Get_Uptime(&Seconds);
		sprintf(texts->text1, "Uptime: %lud %lu:%02lu",(Seconds/(3600*24))%365,(Seconds/3600)%24,(Seconds/60)%60);
	}
}

void clock_update(struct _instance *inst, HWND owner)
{
	if(inst && inst->pixmap && *(inst->pixmap))
	{
		TextConfig *texts = (TextConfig *)inst->custom;
		time_t t = time(NULL);

		strftime(texts->text1, 100, "%I:%M%p", localtime(&t));
	}
}


void drive_update(struct _instance *inst, HWND owner)
{
	if(inst && inst->pixmap && *(inst->pixmap))
	{
	}
}

void DWSIGNAL drive_update_thread(struct _instance *inst)
{
	while(inst && inst->custom && display_active)
	{
		BarConfig *bars = (BarConfig *)inst->custom;
		int drive = DW_POINTER_TO_INT(bars->user);

		if(inst->pixmap && *(inst->pixmap))
		{
			long double free, size;

			free = drivefree(drive);
			size = drivesize(drive);

			dw_mutex_lock(hMtx);
			if(size > 0)
				bars->currentval = 100 - (unsigned long)((free/size)*100.0);
			else
				bars->currentval = 0;

			ScaledPrint(&bars->text[bars->len], free, 1);
			dw_mutex_unlock(hMtx);
		}
		msleep(1000);
	}
}

/* Skip over the floppy drives on OS/2 and Windows */
#if defined(__UNIX__) || defined(__MAC__)
#define DRIVE_START 1
#else
#define DRIVE_START 3
#endif

/* Build monitors for all accessible drives */
void init_drives(void)
{
	int z;

	for(z=DRIVE_START;z<27;z++)
	{
		if(isdrive(z) && drivefree(z) > 0)
		{
			int m = 0;
			BarConfig *bars;
			char fsname[100];

			while(gList[m].Name)
			{
				m++;
			}

			gList[m + 1].Name = NULL;

			gList[m].Name = "Drives";

			gList[m].Update = drive_update;
			gList[m].Create = bar_create;
			gList[m].Draw = bar_draw;
			gList[m].custom = bars = calloc(1, sizeof(BarConfig));

			gList[m].width = 8;
			gList[m].height = 20;
			gList[m].vsize = FALSE;

			bars->currentval = 0;
			bars->maximum = 100;

			getfsname(z, fsname, 100);

			sprintf(bars->text, "%s: ", fsname);

			bars->user = DW_INT_TO_POINTER(z);
			bars->len = strlen(bars->text);

			dw_thread_new((void *)drive_update_thread, &gList[m], 0xFFFF);
		}
	}
}

int dwmain(int argc, char *argv[])
{
	current_font = strdup(DEFFONT);

	loadconfig();

	dw_init(TRUE, argc, argv);

	sockinit();

	init_drives();

	display_create();

	dw_main();

	display_destroy();

	sockshutdown();

	dw_exit(0);
	return 0;
}