// bbapm.cc for bbapm - an tool for display the battery status in X11.
// 
// Copyright (c) 1998 by John Kennis, j.m.b.m.kennis@ele.tue.nl
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
// 
// (See the included file COPYING / GPL-2.0)
// 

#ifdef DEBUG
#include <iostream>
#endif

#include "bbapm.hh"

ToolWindow::ToolWindow(int argc, char **argv):
Basewindow(argc, argv)
{
    resource = NULL;
    load = NULL;

    close_app = False;
    broken = False;

    /*
     * initialize xrm database 
     */
    XrmInitialize();
    resource = new Resource(this);

    load = new GEOM[resource->loadbar.number_of_bars];
    MakeWindow(False);
    ReadAPM();
    Redraw();
}

ToolWindow::~ToolWindow()
{
    XUnmapWindow(dpy, framewin);

    /*
     * destroy pixmaps 
     */
    if (pixmap.frame)
	image_control->removeImage(pixmap.frame);
    if (pixmap.label)
	image_control->removeImage(pixmap.label);
    if (pixmap.loadbar_active)
	image_control->removeImage(pixmap.loadbar_active);
    if (pixmap.loadbar_inactive)
	image_control->removeImage(pixmap.loadbar_inactive);

    if (frameGC)
	XFreeGC(dpy, frameGC);
    XDestroyWindow(dpy, labelwin);
    XDestroyWindow(dpy, framewin);
    delete resource;
}


void
ToolWindow::Reconfigure(void)
{

    /*
     * destroy pixmaps 
     */
    image_control->removeImage(pixmap.frame);
    image_control->removeImage(pixmap.label);
    image_control->removeImage(pixmap.loadbar_active);
    image_control->removeImage(pixmap.loadbar_inactive);
    delete[]load;

    resource->Reload();

    load = new GEOM[resource->loadbar.number_of_bars];
    memset(load, 0, sizeof(GEOM) * resource->loadbar.number_of_bars);

    MakeWindow(True);

    if (resource->show.label)
	XClearWindow(dpy, labelwin);
    XClearWindow(dpy, framewin);
    ReadAPM();
    Redraw();
}

void
ToolWindow::MakeWindow(bool reconfigure)
{
    XSetWindowAttributes
	attrib;
    XWMHints
	wmhints;
    int
	i;
    int
	number_of_loadbars = 0;
    int
	extra_space = 0;
    int
	label_counter = 0;
    unsigned long
	create_mask = CWBackPixmap |	// CWBackPixel|CWBorderPixel|
	CWOverrideRedirect | CWCursor | CWEventMask;

    frame.height =
	resource->frame.font->ascent + resource->frame.font->descent +
	4 * resource->frame.bevelWidth;

    if (resource->show.percentage)
	number_of_loadbars ++;

    label.seperator_width = label.space_width = 0;
    memset(label.width, 0, 8);

    if (resource->show.label) {
	if (resource->show.label_percentage) {
	    label.width[0] = XTextWidth(resource->label.font, "100%",
					strlen("100%"));
	    label_counter++;
	}
	if (resource->show.label_time) {
	    label.width[1] = XTextWidth(resource->label.font,
					"00:00", strlen("00:00"));
	    label_counter++;
	}

	label.seperator_width =
	    XTextWidth(resource->label.font, "/", strlen("/"));
	label.space_width =
	    XTextWidth(resource->label.font, " ",
		       strlen(" ")) + resource->frame.bevelWidth;
	label.height = frame.height - 2 * resource->frame.bevelWidth;
	extra_space = resource->frame.bevelWidth;
    }
    label.total_width = (label_counter - 1) * label.seperator_width +
	2 * label.space_width;
    for (i = 0; i < 8; i++) {
	label.total_width += label.width[i];
    }

#ifdef DEBUG
    cerr << "MakeWindow: computing width " << endl;
#endif

    frame.width = label.total_width + extra_space +
	+resource->frame.bevelWidth * 2 +
	(resource->frame.bevelWidth + resource->loadbar.width) *
	number_of_loadbars;

#ifdef DEBUG
    cerr << "\tframe.width = " << frame.width << endl;
    cerr << "\tnumber_of_loadbars = " << number_of_loadbars << endl;
    cerr << "\tresource.loadbar.width = " << resource->loadbar.width << endl;
#endif

    int
	label_x_position = 0;

    if (number_of_loadbars!=0)
	label_x_position = 2*resource->frame.bevelWidth+
	    (resource->frame.bevelWidth+
	     resource->loadbar.width)*number_of_loadbars;
    else
	label_x_position = resource->frame.bevelWidth+extra_space/2+
	    (resource->frame.bevelWidth+
	     resource->loadbar.width)*number_of_loadbars;
    
    if (resource->position.mask & XNegative) {
	resource->position.x =
	    display_width + resource->position.x - frame.width;
    }

    if (resource->position.mask & YNegative) {
	resource->position.y =
	    display_height + resource->position.y - frame.height;
    }

    attrib.background_pixmap = ParentRelative;

    pixmap.frame =
	image_control->renderImage(frame.width, frame.height,
				   &resource->frame.texture);

    if (withdrawn) {
	attrib.override_redirect = False;
	wmhints.initial_state = WithdrawnState;
    } else {
	attrib.override_redirect = True;
	wmhints.initial_state = NormalState;
    }

    attrib.cursor = cursor;
    attrib.event_mask =
	ButtonPressMask | ButtonReleaseMask | ExposureMask |
	FocusChangeMask | KeyPressMask | StructureNotifyMask;

    if (!reconfigure) {
	framewin = XCreateWindow(dpy, root, resource->position.x,
				 resource->position.y, frame.width,
				 frame.height, 0, depth, InputOutput,
				 v, create_mask, &attrib);
    } else if (!withdrawn) {
	XMoveResizeWindow(dpy, framewin, resource->position.x,
			  resource->position.y, frame.width, frame.height);
    } else {
	XResizeWindow(dpy, framewin, frame.width, frame.height);
    }

    wmhints.flags = IconWindowHint | StateHint;
    wmhints.icon_window = framewin;

    XSetWMHints(dpy, framewin, &wmhints);
    if (!shape) {
	XSetWindowBackgroundPixmap(dpy, framewin, pixmap.frame);
    }


    if (resource->show.label) {
	pixmap.label =
	    image_control->renderImage(label.total_width, label.height,
				       &resource->label.texture);

	if (!reconfigure) {
	    labelwin = XCreateWindow(dpy, framewin, label_x_position,
				     resource->frame.bevelWidth,
				     label.total_width, label.height, 0,
				     depth, InputOutput, v, create_mask,
				     &attrib);
	} else {
	    XMoveResizeWindow(dpy, labelwin,
			      label_x_position,
			      resource->frame.bevelWidth,
			      label.total_width, label.height);
	}

	if (!resource->label.transparent)
	    XSetWindowBackgroundPixmap(dpy, labelwin, pixmap.label);
    }


    pixmap.loadbar_active =
	image_control->renderImage(resource->loadbar.width + 4,
				   frame.height + 4,
				   &resource->loadbar.active_texture);

    pixmap.loadbar_inactive =
	image_control->renderImage(resource->loadbar.width + 4,
				   frame.height + 4,
				   &resource->loadbar.inactive_texture);
    int
	height = 0;
    if (resource->loadbar.number_of_bars != 0)
	height = (int) ceil((frame.height - 2.0 *
			     resource->frame.bevelWidth) /
			    resource->loadbar.number_of_bars);


    for (i = 0; i < resource->loadbar.number_of_bars; i++) {
	load[i].x = (int) (resource->loadbar.left_step_width * i
			   + resource->frame.bevelWidth);
	load[i].y = frame.height - resource->frame.bevelWidth - height * i;
	load[i].width = (int) (resource->loadbar.width -
			       (resource->loadbar.left_step_width +
				resource->loadbar.right_step_width) * i);
	load[i].height = (int) (height * resource->loadbar.ratio);
	load[i].level = (i + 1) * (resource->loadbar.max_time /
				   resource->loadbar.number_of_bars);
    }

    if (!reconfigure) {
	if (resource->show.label) {
	    gcv.font = resource->label.font->fid;
	    frameGC = XCreateGC(dpy, framewin, GCFont, &gcv);
	} else
	    frameGC = XCreateGC(dpy, framewin, 0, &gcv);
    } else {
	if (resource->show.label) {
	    gcv.font = resource->label.font->fid;
	    XChangeGC(dpy, frameGC, GCFont, &gcv);
	} else
	    XChangeGC(dpy, frameGC, 0, &gcv);
    }

    if (!reconfigure) {
	XClearWindow(dpy, framewin);
	XMapSubwindows(dpy, framewin);
	XMapWindow(dpy, framewin);
	mapped = true;
    }
}

void
ToolWindow::ReadAPM()
{
    bool
	redraw = True;

    if (!broken) {

//	if (apm_exists() < 0)
//	    broken = True;
//	else {
	    if ((apm_read(&apm))<0)
		broken = True;
//	}
    }

    if (redraw)
	Redraw();
}

void
ToolWindow::Redraw()
{
    int
	i;
    int
	offset = 0;
    static int noLoadBarFilled = -1; // the number of the loadbar that was drawn last

#ifdef DEBUG
    cerr << "Toolwindow::Redraw called" << endl;
#endif
    // If the battery is loading, reuse the resource.show.percentage
    // loadbar to show that the battery is loading. A bit like GSM. But
    // in this case the label will show how far the loading is done.
    if ((resource->show.loading) &&
	(apm.ai_acline==1) &&
	(resource->show.percentage)) {
	for (i=0; i < resource->loadbar.number_of_bars; i++) {
	    if (noLoadBarFilled>=i) {
		XCopyArea(dpy, pixmap.loadbar_active, framewin, frameGC,
			  2, load[i].y - 1,
			  load[i].width, load[i].height, load[i].x,
			  load[i].y - 1);
	    } else {
		    XCopyArea(dpy, pixmap.loadbar_inactive, framewin, frameGC,
			      2, load[i].y - 1, load[i].width, load[i].height,
			      load[i].x, load[i].y - 1);
	    }
	}
	noLoadBarFilled++;
	if (noLoadBarFilled>resource->loadbar.number_of_bars)
	    noLoadBarFilled = -1;
    } else {
	if (resource->show.percentage) {
#ifdef DEBUG
	    cerr << "Toolwindow::Redraw: drawing percentage " <<
		apm.ai_batt_life << " " <<
		resource->loadbar.number_of_bars << endl;
#endif
	    for (i = 0; i < resource->loadbar.number_of_bars; i++) {
		if (apm.ai_batt_life >= load[i].level)
		    XCopyArea(dpy, pixmap.loadbar_active, framewin, frameGC,
			      2, load[i].y - 1,
			      load[i].width, load[i].height, load[i].x,
			      load[i].y - 1);
		else
		    XCopyArea(dpy, pixmap.loadbar_inactive, framewin, frameGC,
			      2, load[i].y - 1, load[i].width, load[i].height,
			      load[i].x, load[i].y - 1);
	    }
	    offset++;
	}
    }

    if (resource->show.label) {
	char
	    t[6];
	int
	    labelnr = 0;
	int
	    xposition = label.space_width;
	XClearWindow(dpy, labelwin);

	if (resource->show.label_percentage) {
	    labelnr++;
	    XSetForeground(dpy, frameGC,
			   resource->label.percentage_textColor.pixel);

	    sprintf(t, "%03d%%", apm.ai_batt_life);
	    XDrawString(dpy, labelwin, frameGC, label.space_width,
			(label.height + resource->label.font->ascent -
			 resource->label.font->descent) / 2,
			t, strlen(t));
	    xposition += label.width[0];
	}
	if (resource->show.label_time) {
	    if (labelnr != 0) {
		XSetForeground(dpy, frameGC,
			       resource->label.seperator_textColor.pixel);
		sprintf(t, "/");
		XDrawString(dpy, labelwin, frameGC, xposition,
			    (label.height + resource->label.font->ascent
			     - resource->label.font->descent) / 2,
			    t, strlen(t));
		xposition += label.seperator_width;
	    }

	    labelnr++;

	    XSetForeground(dpy, frameGC,
			   resource->label.time_textColor.pixel);

	    sprintf(t, "%02d:%02d", apm.ai_batt_time/3600, (apm.ai_batt_time/60)%60);
	    XDrawString(dpy, labelwin, frameGC, xposition,
			(label.height + resource->label.font->ascent -
			 resource->label.font->descent) / 2, t, strlen(t));
	    xposition += label.width[1];
	}
    }

    if (broken) {
	/*
	 * show broken 
	 */
    }
}

void
ToolWindow::CheckConfig()
{
    struct stat
	file_status;

    if (stat(config_filename, &file_status) != 0) {
	fprintf(stderr, "Could not config file %s\n", config_filename);
    } else if (file_status.st_mtime != resource->style.mtime) {
	resource->style.mtime = file_status.st_mtime;
	Reconfigure();
    }
}

void
ToolWindow::EventLoop(void)
{
    int
	xfd = ConnectionNumber(dpy);
    time_t
	lastTime = time(NULL);
    time_t
	lastLoadingTime = time(NULL);

    while (!close_app) {
	if (XPending(dpy)) {
	    XEvent
		Event;
	    XNextEvent(dpy, &Event);
	    /*
	     * process events 
	     */
	    switch (Event.type) {
	    case Expose:
		{
		    Redraw();
		}
		break;
	    case ButtonPress:
		{
		    if (Event.xbutton.button == LEFT_BUTTON) {
			if (!(raised)) {
			    XRaiseWindow(dpy, framewin);
			    raised = True;
			}
		    } else if (Event.xbutton.button == MIDDLE_BUTTON) {
			if (raised) {
			    XLowerWindow(dpy, framewin);
			    raised = False;
			}
		    }
		}
		break;
	    case ButtonRelease:
		{
		    if (Event.xbutton.button == RIGHT_BUTTON) {
			if (Event.xbutton.window == labelwin) {
			    Reconfigure();
			}
		    }

		}
		break;
	    case ConfigureNotify:
		{
		    if (Event.xconfigure.window == framewin
			&& Event.xconfigure.send_event) {
			Reconfigure();
			int
			    parent_x,
			    parent_y;
			Window
			    parent_root;
			unsigned int
			    parent_width;
			unsigned int
			    parent_height;
			unsigned int
			    parent_border_width;
			unsigned int
			    parent_depth;
			XGetGeometry(dpy, Event.xconfigure.above,
				     &parent_root, &parent_x, &parent_y,
				     &parent_width, &parent_height,
				     &parent_border_width, &parent_depth);
			frame.x = Event.xconfigure.x + parent_x;
			frame.y = Event.xconfigure.y + parent_y;
		    }
		}
	    }

	    if (apm.ai_acline==1) {
		if (time(NULL) - lastLoadingTime > resource->show.loadingTime) {
		    ReadAPM();
		    lastLoadingTime = time(NULL);
		}
	    }
	    if (time(NULL) - lastTime > resource->report.check_delay - 1) {
		ReadAPM();
		if (resource->style.auto_config && !withdrawn) {
		    CheckConfig();
		}
		lastTime = time(NULL);
	    }
	} else {
	    // put a wait on the network file descriptor for the X
	    // connection...
	    // this saves blackbox from eating all available cpu

	    if (time(NULL) - lastTime < resource->show.loadingTime) {
		fd_set
		    rfds;
		FD_ZERO(&rfds);
		FD_SET(xfd, &rfds);

		struct timeval
		    tv;
		tv.tv_sec =
		    resource->show.loadingTime - (time(NULL) - lastTime);
		tv.tv_usec = 0;

		select(xfd + 1, &rfds, 0, 0, &tv);
	    } else {
		// this will be done every check_delay seconds
		ReadAPM();
		if (resource->style.auto_config) {
		    CheckConfig();
		}
		lastTime = time(NULL);
	    }
	}
    }
}

int
main(int argc, char **argv)
{
    /* APM initialization */
    apm_exists();

    ToolWindow     *
	AppWindow = new ToolWindow(argc, argv);

    AppWindow->EventLoop();
    delete
	AppWindow;
}


syntax highlighted by Code2HTML, v. 0.9.1