/*
 * asapm is the APM (Advanced Power Management) monitor utility for X Windows
 * Copyright (c) 1998-99  Albert Dorofeev <Albert@mail.dma.be>
 * For the updates see http://bewoner.dma.be/Albert/linux/
 *
 * This software is distributed under GPL. For details see LICENSE file.
 */

#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>

#include <X11/Xlib.h>
#include <X11/xpm.h>
#include <X11/Xatom.h>

#include "background_in.xpm"
#include "background_out.xpm"
#include "alphabet_in.xpm"
#include "alphabet_out.xpm"
#include "state.h"

extern struct apm_state state;

/* nice idea from ascd */
typedef struct _XpmIcon {
    Pixmap pixmap;
    Pixmap mask;
    XpmAttributes attributes;
} XpmIcon;

XpmIcon	backgroundXpm;
XpmIcon alphabetXpm;
char alphabetColor[12];
char alphabetLowColor[12];
char BGColor8bpp[9][12];

int alphabetPosition[15] = {
		0, 7, 14, 21, 28, 35, 42, 49, 56, 63, 70, 78, 82, 90, 94
	};
int alphabetSize[15] = {
		8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 4, 8, 4, 7
	};
int percentPosition[4] = { 13, 21, 29, 37 };
int timePosition[5] = { 12, 19, 26, 30, 37 };

#define BATTERY_IMAGE_X 14
#define BATTERY_IMAGE_Y 4
XPoint batteryImage[9] = {
		{ BATTERY_IMAGE_X+12, BATTERY_IMAGE_Y+4 },
		{ BATTERY_IMAGE_X+12, BATTERY_IMAGE_Y+1 },
		{ BATTERY_IMAGE_X+13, BATTERY_IMAGE_Y+1 },
		{ BATTERY_IMAGE_X+13, BATTERY_IMAGE_Y+4 },
		{ BATTERY_IMAGE_X+11, BATTERY_IMAGE_Y+4 },
		{ BATTERY_IMAGE_X+11, BATTERY_IMAGE_Y+0 },
		{ BATTERY_IMAGE_X+0, BATTERY_IMAGE_Y+0 },
		{ BATTERY_IMAGE_X+0, BATTERY_IMAGE_Y+5 },
		{ BATTERY_IMAGE_X+12, BATTERY_IMAGE_Y+5 }
	};

#define AC_IMAGE_X 31
#define AC_IMAGE_Y 4
XPoint acImage[28] = {
		{ AC_IMAGE_X+0, AC_IMAGE_Y+2 },
		{ AC_IMAGE_X+1, AC_IMAGE_Y+1 },
		{ AC_IMAGE_X+2, AC_IMAGE_Y+1 },
		{ AC_IMAGE_X+3, AC_IMAGE_Y+2 },
		{ AC_IMAGE_X+4, AC_IMAGE_Y+2 },
		{ AC_IMAGE_X+4, AC_IMAGE_Y+3 },
		{ AC_IMAGE_X+5, AC_IMAGE_Y+1 },
		{ AC_IMAGE_X+5, AC_IMAGE_Y+2 },
		{ AC_IMAGE_X+5, AC_IMAGE_Y+3 },
		{ AC_IMAGE_X+5, AC_IMAGE_Y+4 },
		{ AC_IMAGE_X+6, AC_IMAGE_Y+0 },
		{ AC_IMAGE_X+7, AC_IMAGE_Y+0 },
		{ AC_IMAGE_X+8, AC_IMAGE_Y+0 },
		{ AC_IMAGE_X+9, AC_IMAGE_Y+0 },
		{ AC_IMAGE_X+10, AC_IMAGE_Y+0 },
		{ AC_IMAGE_X+10, AC_IMAGE_Y+1 },
		{ AC_IMAGE_X+10, AC_IMAGE_Y+2 },
		{ AC_IMAGE_X+10, AC_IMAGE_Y+3 },
		{ AC_IMAGE_X+10, AC_IMAGE_Y+4 },
		{ AC_IMAGE_X+10, AC_IMAGE_Y+5 },
		{ AC_IMAGE_X+9, AC_IMAGE_Y+5 },
		{ AC_IMAGE_X+8, AC_IMAGE_Y+5 },
		{ AC_IMAGE_X+7, AC_IMAGE_Y+5 },
		{ AC_IMAGE_X+6, AC_IMAGE_Y+5 },
		{ AC_IMAGE_X+11, AC_IMAGE_Y+1 },
		{ AC_IMAGE_X+12, AC_IMAGE_Y+1 },
		{ AC_IMAGE_X+11, AC_IMAGE_Y+4 },
		{ AC_IMAGE_X+12, AC_IMAGE_Y+4 }
	};

/* X windows related global variables */
Display * mainDisplay = 0;	/* The display we are working on */
Window Root;			/* The root window of X11 */
Window mainWindow;		/* Application window */
Window iconWindow;		/* Icon window */
XGCValues mainGCV;		/* graphics context values */
GC mainGC;			/* Graphics context */
Pixel greenPixel, redPixel;
Pixel lightgreenPixel, lightredPixel;
Pixel semilightgreenPixel, semilightredPixel;
Pixel bitlightgreenPixel, bitlightredPixel;
Pixel darkgreenPixel, darkredPixel;
Pixel semidarkgreenPixel, semidarkredPixel;
Pixel bitdarkgreenPixel, bitdarkredPixel;
Pixel greenTextPixel, yellowTextPixel, redTextPixel;
Atom wm_delete_window;
Atom wm_protocols;

/*
 * This function clears up all X related
 * stuff and exits. It is called in case
 * of emergencies too.
 */
void Cleanup()
{
	if ( mainDisplay )
	{
		XCloseDisplay(mainDisplay);
	}
	exit(0);
}

/*
 * this function was taken out of ascd,
 * it seems to translate the given name
 * into the corresponding color
 */
Pixel GetColor(char *ColorName)
{
    XColor Color;
    XWindowAttributes Attributes;

    XGetWindowAttributes(mainDisplay,Root,&Attributes);
    Color.pixel = 0;

    if (!XParseColor (mainDisplay, Attributes.colormap, ColorName, &Color))
         printf("asapm: can't parse %s\n", ColorName);
    else if(!XAllocColor (mainDisplay, Attributes.colormap, &Color))
         printf("asapm: can't allocate %s\n", ColorName);

    return Color.pixel;
}

/* parses the given color */
XColor ParseColor(char *ColorName)
{
    XColor Color;
    XWindowAttributes Attributes;

    XGetWindowAttributes(mainDisplay,Root,&Attributes);
    Color.pixel = 0;

    if (!XParseColor (mainDisplay, Attributes.colormap, ColorName, &Color))
         printf("asapm: can't parse %s\n", ColorName);
    else if(!XAllocColor (mainDisplay, Attributes.colormap, &Color))
         printf("asapm: can't allocate %s\n", ColorName);

    return Color;
}

/* darkens the given color using the supplied rate */
Pixel DarkenColor(char *ColorName, float rate)
{
	XColor tmp_color;
	char tmp_char[50];
#ifdef DEBUG
	printf("darkening %s ->", ColorName);
#endif
	tmp_color = ParseColor(ColorName);
#ifdef DEBUG
	printf(" #%x %x %x ", tmp_color.red, tmp_color.green, tmp_color.blue);
#endif
	tmp_color.red = tmp_color.red / 257 / rate;
	tmp_color.green = tmp_color.green / 257 / rate;
	tmp_color.blue = tmp_color.blue / 257 / rate;
	sprintf(tmp_char, "#%.2x%.2x%.2x", 
		(int) tmp_color.red,
		(int) tmp_color.green,
		(int) tmp_color.blue);
#ifdef DEBUG
	printf("-> %s\n", tmp_char);
#endif
	return GetColor(tmp_char);
}

Pixel LightenColor(char *ColorName, float rate)
{
	XColor tmp_color;
	char tmp_char[50];
#ifdef DEBUG
	printf("lightening %s ->", ColorName);
#endif
	tmp_color = ParseColor(ColorName);
#ifdef DEBUG
	printf(" #%x %x %x ", tmp_color.red, tmp_color.green, tmp_color.blue);
#endif
	tmp_color.red = tmp_color.red / 257 * rate;
	tmp_color.green = tmp_color.green / 257 * rate;
	tmp_color.blue = tmp_color.blue / 257 * rate;
	if (tmp_color.red > 255) tmp_color.red = 255;
	if (tmp_color.green > 255) tmp_color.green = 255;
	if (tmp_color.blue > 255) tmp_color.blue = 255;
	sprintf(tmp_char, "#%.2x%.2x%.2x", 
		(int) tmp_color.red,
		(int) tmp_color.green,
		(int) tmp_color.blue);
#ifdef DEBUG
	printf("-> %s\n", tmp_char);
#endif
	return GetColor(tmp_char);
}

void Redraw();
/*
 * Initialize X specific global variables
 * and create two windows. One window is
 * the main application window, the other
 * serves as an icon for the first (although
 * they are exactly identical in drawing).
 */
void initializeWindow(int argc, char** argv,
			char * display_name,
			char * mainGeometry,
			char * statuscolor,
			char * greencolor,
			char * yellowcolor,
			char * redcolor,
			char * slidercolor,
			char * sliderbgcolor,
			char * app_name,
			int withdrawn,
			int iconic,
			int embossed)
{
	int screen;
	Status status;
	XWindowAttributes winAttr;
	Pixel back_pix, fore_pix;
	XSizeHints SizeHints;
	XTextProperty title;
	XClassHint classHint;
	int gravity;
	XWMHints WmHints;
	XEvent Event;
	XColor tmp_color;
	char ** background;
	char ** alphabet;
	int color_depth;
	int tmp;
	int result;
	int x_negative = 0;
	int y_negative = 0;
/*	XIconSize * xiconsize; */

	mainDisplay = XOpenDisplay(display_name);
	if ( ! mainDisplay ) {
		printf("asapm : can't open display %s. Sorry ...\n", 
			XDisplayName(display_name));
		exit(1);
	}
	screen = DefaultScreen(mainDisplay);
	Root = RootWindow(mainDisplay, screen);
	back_pix = GetColor("white");
	fore_pix = GetColor("black");
	color_depth = DefaultDepth(mainDisplay, screen);
	if ( ! state.color_depth )
		state.color_depth = color_depth;
#ifdef DEBUG
	printf("Detected color depth %d bpp, using %d bpp\n", 
		color_depth, state.color_depth);
#endif

	backgroundXpm.attributes.valuemask |= 
		(XpmReturnPixels | XpmReturnExtensions
		| XpmExactColors | XpmCloseness);
	backgroundXpm.attributes.exactColors = False;
	backgroundXpm.attributes.closeness = 65536;
	background = embossed ? background_in : background_out;
	if ( state.color_depth == 8 ) {
		/* degrade the background */
		sprintf(BGColor8bpp[0], "r c #303030");
		sprintf(BGColor8bpp[1], "s c #303030");
		sprintf(BGColor8bpp[2], "t c #303030");
		sprintf(BGColor8bpp[3], "u c #303030");
		sprintf(BGColor8bpp[4], "v c #303030");
		sprintf(BGColor8bpp[5], "w c #303030");
		sprintf(BGColor8bpp[6], "x c #303030");
		sprintf(BGColor8bpp[7], "a c #202020");
		sprintf(BGColor8bpp[8], "b c #202020");
		for (tmp = 0; tmp < 7; ++tmp)
			background[tmp+7] = BGColor8bpp[tmp];
		background[2] = BGColor8bpp[7];
		background[3] = BGColor8bpp[8];
	}
	status = XpmCreatePixmapFromData(
		mainDisplay,            	/* display */
		Root,             		/* window */
		background,			/* xpm */
		&backgroundXpm.pixmap,		/* resulting pixmap */
		&backgroundXpm.mask,
		&backgroundXpm.attributes
		);
	if(status != XpmSuccess) {
		printf("asapm : (%d) not enough free color cells for background.\n", status);
		XCloseDisplay(mainDisplay);
		exit(1);
	}

	if (strlen(mainGeometry)) {
		/* Check the user-specified size */
		result = XParseGeometry(
			mainGeometry,
			&SizeHints.x,
			&SizeHints.y,
			&SizeHints.width,
			&SizeHints.height
			);
		if (result & XNegative) 
			x_negative = 1;
		if (result & YNegative) 
			y_negative = 1;
	}
	SizeHints.flags= USSize|USPosition;
	SizeHints.x = 0;
	SizeHints.y = 0;
	XWMGeometry(
		mainDisplay,
		screen,
		mainGeometry,		/* user_geometry */
		NULL,			/* default_geometry */
		1,			/* border_width */
		& SizeHints,		/* hints */
		&SizeHints.x,		/* x_return */
		&SizeHints.y,		/* y_return */
		&SizeHints.width,	/* width_return */
		&SizeHints.height,	/* height_return */
		&gravity		/* gravity_return */
		);
/*	printf("Icon size: min %dx%d max %dx%d WM req %dx%d\n",
		SizeHints.min_width, SizeHints.min_height,
		SizeHints.max_width, SizeHints.max_height,
		SizeHints.width, SizeHints.height);	*/
	SizeHints.min_width = 
	SizeHints.max_width =
	SizeHints.width = backgroundXpm.attributes.width;
	SizeHints.min_height =
	SizeHints.max_height =
	SizeHints.height= backgroundXpm.attributes.height;
	SizeHints.flags |= PMinSize|PMaxSize;

	/* Correct the offsets if the X/Y are negative */
	SizeHints.win_gravity = NorthWestGravity;
	if (x_negative) {
		SizeHints.x -= SizeHints.width;
		SizeHints.win_gravity = NorthEastGravity;
	}
	if (y_negative) {
		SizeHints.y -= SizeHints.height;
		if (x_negative)
			SizeHints.win_gravity = SouthEastGravity;
		else
			SizeHints.win_gravity = SouthWestGravity;
	}
	SizeHints.flags |= PWinGravity;

	mainWindow = XCreateSimpleWindow(
		mainDisplay,		/* display */
		Root,			/* parent */
		SizeHints.x,		/* x */
		SizeHints.y,		/* y */
		SizeHints.width,	/* width */
		SizeHints.height,	/* height */
		0,			/* border_width */
		fore_pix,		/* border */
		back_pix		/* background */
		);

	iconWindow = XCreateSimpleWindow(
		mainDisplay,		/* display */
		Root,			/* parent */
		SizeHints.x,		/* x */
		SizeHints.y,		/* y */
		SizeHints.width,	/* width */
		SizeHints.height,	/* height */
		0,			/* border_width */
		fore_pix,		/* border */
		back_pix		/* background */
		);

	XSetWMNormalHints(mainDisplay, mainWindow, &SizeHints);
	XSetWMNormalHints(mainDisplay, iconWindow, &SizeHints);
	status = XClearWindow(mainDisplay, mainWindow);
	status = XClearWindow(mainDisplay, iconWindow);

	/*xiconsize = XAllocIconSize();
	xiconsize->min_width = 
	xiconsize->max_width = SizeHints.width;
	xiconsize->min_height =
	xiconsize->max_height = SizeHints.height;
	XSetIconSizes(mainDisplay,mainWindow,xiconsize,1);
	XSetIconSizes(mainDisplay,iconWindow,xiconsize,1);
	status = XGetIconSizes( mainDisplay,mainWindow,&xiconsize,&tmp );
	printf("Got icon size: min %dx%d max %dx%d\n",
		xiconsize->min_width, xiconsize->min_height,
		xiconsize->max_width, xiconsize->max_height);
	printf("Status %d, count %d\n", status, tmp);*/

	status = XGetWindowAttributes(
		mainDisplay,	/* display */
		mainWindow,	/* window */
		& winAttr	/* window_attributes_return */
		);
	status = XSetWindowBackgroundPixmap(
		mainDisplay,            /* display */
		mainWindow,             /* window */
		backgroundXpm.pixmap	/* background_pixmap */
		);
	status = XSetWindowBackgroundPixmap(
		mainDisplay,            /* display */
		iconWindow,             /* window */
		backgroundXpm.pixmap	/* background_pixmap */
		);

	status = XStringListToTextProperty(&app_name, 1, &title);
	XSetWMName(mainDisplay, mainWindow, &title);
	XSetWMName(mainDisplay, iconWindow, &title);

	classHint.res_name = "asapm" ;
	classHint.res_class = "ASAPM";
	XSetClassHint(mainDisplay, mainWindow, &classHint);
	XStoreName(mainDisplay, mainWindow, "asapm");
	XSetIconName(mainDisplay, mainWindow, "asapm");

	status = XSelectInput(
		mainDisplay, 	/* display */
		mainWindow, 	/* window */
		ExposureMask	/* event_mask */
		);

	status = XSelectInput(
		mainDisplay, 	/* display */
		iconWindow, 	/* window */
		ExposureMask	/* event_mask */
		);

	/* Creating Graphics context */
	mainGCV.foreground = fore_pix;
	mainGCV.background = back_pix;
	mainGCV.graphics_exposures = False;
	mainGCV.line_style = LineSolid;
	mainGCV.fill_style = FillSolid;
	mainGCV.line_width = 1;
	mainGC = XCreateGC(
		mainDisplay, 
		mainWindow, 
		GCForeground|GCBackground|GCLineWidth|
			GCLineStyle|GCFillStyle,
		&mainGCV
		);

	status = XSetCommand(mainDisplay, mainWindow, argv, argc);

	/* Set up the event for quitting the window */
	wm_delete_window = XInternAtom(
		mainDisplay, 
		"WM_DELETE_WINDOW",	/* atom_name */
		False			/* only_if_exists */
		);
	wm_protocols = XInternAtom(
		mainDisplay,
		"WM_PROTOCOLS",		/* atom_name */
		False			/* only_if_exists */
		);
	status = XSetWMProtocols(
		mainDisplay, 
		mainWindow,
		&wm_delete_window, 
		1
		);
	status = XSetWMProtocols(
		mainDisplay, 
		iconWindow,
		&wm_delete_window, 
		1
		);

	WmHints.flags = StateHint | IconWindowHint;
	WmHints.initial_state = 
		withdrawn ? WithdrawnState :
			iconic ? IconicState : NormalState;
	WmHints.icon_window = iconWindow;
	if ( withdrawn ) {
		WmHints.window_group = mainWindow;
		WmHints.flags |= WindowGroupHint;
	}
	if ( iconic || withdrawn ) {
		WmHints.icon_x = SizeHints.x;
		WmHints.icon_y = SizeHints.y;
		WmHints.flags |= IconPositionHint;
	}
	XSetWMHints(
		mainDisplay, 
		mainWindow, 
		&WmHints);

	/* Finally show the window */
	status = XMapWindow(
		mainDisplay,
		mainWindow
		);

	/* get the colors */
	greenPixel = GetColor(slidercolor);
	redPixel = GetColor(sliderbgcolor);
	greenTextPixel = GetColor(greencolor);
	yellowTextPixel = GetColor(yellowcolor);
	redTextPixel = GetColor(redcolor);

	if ( state.color_depth == 8 ) {
		darkgreenPixel = greenPixel;
		semidarkgreenPixel = greenPixel;
		bitdarkgreenPixel = greenPixel;
		darkredPixel = redPixel;
		semidarkredPixel = redPixel;
		bitdarkredPixel = redPixel;
		lightgreenPixel = greenPixel;
		semilightgreenPixel = greenPixel;
		bitlightgreenPixel = greenPixel;
		lightredPixel = redPixel;
		semilightredPixel = redPixel;
		bitlightredPixel = redPixel;
	} else {
		/* scale the colors */
		darkgreenPixel = DarkenColor(slidercolor, 1.4);
		semidarkgreenPixel = DarkenColor(slidercolor, 1.2);
		bitdarkgreenPixel = DarkenColor(slidercolor, 1.1);
		darkredPixel = DarkenColor(sliderbgcolor, 1.4);
		semidarkredPixel = DarkenColor(sliderbgcolor, 1.2);
		bitdarkredPixel = DarkenColor(sliderbgcolor, 1.1);
		lightgreenPixel = LightenColor(slidercolor, 1.4);
		semilightgreenPixel = LightenColor(slidercolor, 1.2);
		bitlightgreenPixel = LightenColor(slidercolor, 1.1);
		lightredPixel = LightenColor(sliderbgcolor, 1.4);
		semilightredPixel = LightenColor(sliderbgcolor, 1.2);
		bitlightredPixel = LightenColor(sliderbgcolor, 1.1);
	}

	alphabet = embossed ? alphabet_in : alphabet_out;
	/* adjust the color for digits */
	tmp_color = ParseColor(statuscolor);
	sprintf(alphabetColor, "# c #%.2x%.2x%.2x",
		(int) (tmp_color.red / 257),
		(int) (tmp_color.green / 257),
		(int) (tmp_color.blue / 257));
	alphabet[2] = alphabetColor;
#ifdef DEBUG
	printf("alphabet color [%s]\n", alphabetColor);
#endif
	if ( state.color_depth == 8 ) {
		sprintf(alphabetLowColor, "c c #303030");
		alphabet[4] = alphabetLowColor;
	} else {
		sprintf(alphabetLowColor, "c c #%.2x%.2x%.2x",
			(int) (tmp_color.red / 257 / 1.5),
			(int) (tmp_color.green / 257 / 1.6),
			(int) (tmp_color.blue / 257 / 1.6));
		alphabet[4] = alphabetLowColor;
	}
#ifdef DEBUG
	printf("alphabet low color [%s]\n", alphabetLowColor);
#endif

	alphabetXpm.attributes.valuemask |= 
		(XpmReturnPixels | XpmReturnExtensions
		| XpmExactColors | XpmCloseness);
	alphabetXpm.attributes.exactColors = False;
	alphabetXpm.attributes.closeness = 65536;
	status = XpmCreatePixmapFromData(
		mainDisplay,            	/* display */
		Root,             		/* window */
		alphabet,			/* xpm */
		&alphabetXpm.pixmap,		/* resulting pixmap */
		&alphabetXpm.mask,
		&alphabetXpm.attributes
		);
	if(status != XpmSuccess) {
		printf("asapm : (%d) not enough free color cells for alphabet.\n", status);
		XCloseDisplay(mainDisplay);
		exit(1);
	}

	/* wait for the Expose event now */
	XNextEvent(mainDisplay, &Event);
	/* We 've got Expose -> draw the parts of the window. */
	Redraw();
	XFlush(mainDisplay);
}

/*
 * Draw the slider bar. The slider bar has two
 * colors. The bottom side shows the percent left
 * of the battery's charge (greenPixel) and
 * the top shows the rest (redPixel)
 */
void DrawIndicator(Window win)
{
	int points;
	if (state.percent < 0) {
		/* clear previously drawn label */
		XClearArea(
			mainDisplay,
			win,
			2,
			4,
			7,
			42,
			False
			);
		return;
	}
	points = (state.percent==100) ? 42 : (42.0/100 * state.percent);
	/* set green color */
	mainGCV.foreground = greenPixel;
	XChangeGC(
		mainDisplay,
		mainGC,
		GCForeground,
		&mainGCV
		);
	/* draw the bottom part of the indicator */
	XFillRectangle(
		mainDisplay,
		win,
		mainGC,
		5,
		4+42-points, /*4,*/
		1,
		points
		);
	mainGCV.foreground = darkgreenPixel;
	XChangeGC(
		mainDisplay,
		mainGC,
		GCForeground,
		&mainGCV
		);
	XFillRectangle(
		mainDisplay,
		win,
		mainGC,
		8,
		4+42-points, /*4,*/
		1,
		points
		);
	mainGCV.foreground = semidarkgreenPixel;
	XChangeGC(
		mainDisplay,
		mainGC,
		GCForeground,
		&mainGCV
		);
	XFillRectangle(
		mainDisplay,
		win,
		mainGC,
		7,
		4+42-points, /*4,*/
		1,
		points
		);
	mainGCV.foreground = bitdarkgreenPixel;
	XChangeGC(
		mainDisplay,
		mainGC,
		GCForeground,
		&mainGCV
		);
	XFillRectangle(
		mainDisplay,
		win,
		mainGC,
		6,
		4+42-points, /*4,*/
		1,
		points
		);
	mainGCV.foreground = lightgreenPixel;
	XChangeGC(
		mainDisplay,
		mainGC,
		GCForeground,
		&mainGCV
		);
	XFillRectangle(
		mainDisplay,
		win,
		mainGC,
		2,
		4+42-points, /*4,*/
		1,
		points
		);
	mainGCV.foreground = semilightgreenPixel;
	XChangeGC(
		mainDisplay,
		mainGC,
		GCForeground,
		&mainGCV
		);
	XFillRectangle(
		mainDisplay,
		win,
		mainGC,
		3,
		4+42-points, /*4,*/
		1,
		points
		);
	mainGCV.foreground = bitlightgreenPixel;
	XChangeGC(
		mainDisplay,
		mainGC,
		GCForeground,
		&mainGCV
		);
	XFillRectangle(
		mainDisplay,
		win,
		mainGC,
		4,
		4+42-points, /*4,*/
		1,
		points
		);
	/* set red color */
	mainGCV.foreground = redPixel;
	XChangeGC(
		mainDisplay,
		mainGC,
		GCForeground,
		&mainGCV
		);
	/* draw right side of the indicator */
	XFillRectangle(
		mainDisplay,
		win,
		mainGC,
		3,
		4, /*4+points,*/
		4,
		42-points
		);
	mainGCV.foreground = lightredPixel;
	XChangeGC(
		mainDisplay,
		mainGC,
		GCForeground,
		&mainGCV
		);
	XFillRectangle(
		mainDisplay,
		win,
		mainGC,
		8,
		4, /*4+points,*/
		1,
		42-points
		);
	mainGCV.foreground = semilightredPixel;
	XChangeGC(
		mainDisplay,
		mainGC,
		GCForeground,
		&mainGCV
		);
	XFillRectangle(
		mainDisplay,
		win,
		mainGC,
		7,
		4, /*4+points,*/
		1,
		42-points
		);
	mainGCV.foreground = bitlightredPixel;
	XChangeGC(
		mainDisplay,
		mainGC,
		GCForeground,
		&mainGCV
		);
	XFillRectangle(
		mainDisplay,
		win,
		mainGC,
		6,
		4, /*4+points,*/
		1,
		42-points
		);
	mainGCV.foreground = darkredPixel;
	XChangeGC(
		mainDisplay,
		mainGC,
		GCForeground,
		&mainGCV
		);
	XFillRectangle(
		mainDisplay,
		win,
		mainGC,
		2,
		4, /*4+points,*/
		1,
		42-points
		);
	mainGCV.foreground = semidarkredPixel;
	XChangeGC(
		mainDisplay,
		mainGC,
		GCForeground,
		&mainGCV
		);
	XFillRectangle(
		mainDisplay,
		win,
		mainGC,
		3,
		4, /*4+points,*/
		1,
		42-points
		);
	mainGCV.foreground = bitdarkredPixel;
	XChangeGC(
		mainDisplay,
		mainGC,
		GCForeground,
		&mainGCV
		);
	XFillRectangle(
		mainDisplay,
		win,
		mainGC,
		4,
		4, /*4+points,*/
		1,
		42-points
		);
}

void DrawBattery( Window win )
{
	Pixel theColor;
	switch ( state.battery_status )
	{
	case BATTERY_CRITICAL:
		/* third is "red" */
		theColor = redTextPixel;
		break;
	case BATTERY_LOW:
		/* second is "yellow" */
		theColor = yellowTextPixel;
		break;
	case BATTERY_UNKNOWN:
		theColor = BlackPixel(mainDisplay, DefaultScreen(mainDisplay));
		break;
	case BATTERY_HIGH:
	case BATTERY_CHARGING:
	default:
		/* first is "green" */
		theColor = greenTextPixel;
		break;
	}
	/* set the chosen color */
	mainGCV.foreground = theColor;
	XChangeGC(
		mainDisplay,
		mainGC,
		GCForeground,
		&mainGCV
		);
	/* draw the battery image */
	XDrawLines(
		mainDisplay,
		win,
		mainGC,
		batteryImage,
		9,
		CoordModeOrigin
		);
	/* clear previously drawn label */
	XClearArea(
		mainDisplay,
		win,
		AC_IMAGE_X,
		AC_IMAGE_Y,
		13,
		6,
		False
		);
	if ( state.ac_line_status == AC_ONLINE )
	{
		if ( state.battery_status == BATTERY_CHARGING ) {
			/* set red color */
			mainGCV.foreground = redTextPixel;
		} else {
			/* set green color */
			mainGCV.foreground = greenTextPixel;
		}
		XChangeGC(
			mainDisplay,
			mainGC,
			GCForeground,
			&mainGCV
			);
		XDrawPoints(
			mainDisplay,
			win,
			mainGC,
			acImage,
			28,
			CoordModeOrigin
			);
	}
}

void DrawPercent(Window win)
{
	int tmp[4];
	int i;
	if ( state.percent > 0 ) {
		if ( state.percent / 100 )
			tmp[0] = state.percent / 100;
		else
			tmp[0] = 10;
		tmp[1] = state.percent % 100 / 10;
		tmp[2] = state.percent % 100 % 10;
		tmp[3] = 12;
	} else {
		tmp[0] = 10;
		tmp[1] = 10;
		tmp[2] = 10;
		tmp[3] = 14;
	}
	for (i=0; i<4; ++i)
	XCopyArea(
		mainDisplay,
		alphabetXpm.pixmap,
		win,
		mainGC,
		alphabetPosition[tmp[i]],
		0,
		alphabetSize[tmp[i]],
		11,
		percentPosition[i],
		16
		);
}

void DrawTime(Window win)
{
	int tmp[5];
	int i;
	int firstDigit = 0;
	int time_left;
	if ( (state.time_left > 0) && (state.system_time) )
		time_left = state.time_left;
	else
		time_left = state.time_estimate;
	if ( time_left > 0 ) {
		if ( time_left / 60 / 10 % 10 ) {
			tmp[0] = time_left / 60 / 10 % 10;
			firstDigit = 1;
		} else
			tmp[0] = 10;
		if ( firstDigit || (time_left / 60 % 10) )
			tmp[1] = time_left / 60 % 10;
		else
			tmp[1] = 10;
		tmp[2] = 11;
		tmp[3] = time_left % 60 / 10;
		tmp[4] = time_left % 60 % 10;
	} else {
		tmp[0] = 10;
		tmp[1] = 10;
		tmp[2] = 13;
		tmp[3] = 10;
		tmp[4] = 10;
	}
	for (i=0; i<5; ++i)
	XCopyArea(
		mainDisplay,
		alphabetXpm.pixmap,
		win,
		mainGC,
		alphabetPosition[tmp[i]],
		0,
		alphabetSize[tmp[i]],
		11,
		timePosition[i],
		33
		);
}

/* This requests redrawing of all elements */
void Redraw()
{
	DrawIndicator(mainWindow);
	DrawBattery(mainWindow);
	DrawPercent(mainWindow);
	DrawTime(mainWindow);
	DrawIndicator(iconWindow);
	DrawBattery(iconWindow);
	DrawPercent(iconWindow);
	DrawTime(iconWindow);
}

/*
 * This checks for X11 events. We distinguish the following:
 * - request to repaint the window
 * - request to quit (Close button)
 */
void CheckX11Events()
{
	XEvent Event;
	while (XPending(mainDisplay)) {
		XNextEvent(mainDisplay, &Event);
		switch(Event.type)
		{
		case Expose:
			if(Event.xexpose.count == 0)
				++state.update;
			break;
		case ClientMessage:
			if ((Event.xclient.message_type == wm_protocols)
			  && (Event.xclient.data.l[0] == wm_delete_window))
				Cleanup();
			break;
		}
	}
}



syntax highlighted by Code2HTML, v. 0.9.1