/*

 wmcube.c 
 Version 0.98 (2000-10-20)

 Robert Kling (robkli-8@student.luth.se)
 http://boombox.campus.luth.se/projects.php

 Contributions:
 	-n option patch by Thorsten Jens (thodi@et-inf.fho-emden.de) (2000-05-12)
 	Various bugfixes and optimizations by Jakob Borg (2000-05-13)
	Solaris Port by Dan Price (dp@rampant.org) (2000-07-16)
	OpenBSD Port by Brian Joseph Czapiga (rys@godsey.net) (2000-07-19)
	FreeBSD Port by Tai-hwa Liang (avatar@mmlab.cse.yzu.edu.tw) (2000-07-20)
	NetBSD Port by Jared Smolens <jsmolens+@andrew.cmu.edu> (2000-09-23)
	gdk gfx, dynamic colors, etc timecop@japan.co.jp (2001-02-13)

 This software is licensed through the GNU General Public Licence.
 
 See http://www.BenSinclair.com/dockapp/ for more wm dock apps.

 If you want to port wmcube to another OS the system specific code is
 sectioned the bottom of this file. See instructions there.

*/

#define _GNU_SOURCE
#define WMCUBE_VERSION "0.99"
#define REV_DATE "2001-02-13"

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <math.h>

#ifdef LINUX
/* forgotten includes */
#include <getopt.h>
#include <dirent.h>
#endif

#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>

#include <utmp.h>
#include <dirent.h>

#include <gdk/gdk.h>
#include <gdk/gdkx.h>

#ifdef FREEBSD
#include <kvm.h>
#endif

/* this is a somewhat compressed version of "digits[96 * 9]"
 * each 1/4 hex digit is a colormap entry. */
/* *INDENT-OFF* */
static unsigned char c0de[24 * 9 + 1] =
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x05\x40\x08\x25\x62\x56\x20\x22\x56\x25\x62\x56\x25\x62\x56\x00\x00\x00\x00\x00\x03\xc0\x0a\x00"
"\x10\x10\x04\x00\x10\x01\x10\x11\x00\x10\x00\x01\x10\x11\x01\x10\x10\x00\x00\x00\x03\xc0\x0a\x00"
"\x10\x10\x04\x00\x10\x01\x10\x11\x00\x10\x00\x01\x10\x11\x01\x00\x43\xff\xca\xaa\x3f\xfc\xaa\xa0"
"\x20\x20\x08\x25\x60\x56\x25\x62\x56\x25\x60\x02\x35\x72\x56\x01\x03\xff\xca\xaa\x3f\xfc\xaa\xa0"
"\x10\x10\x04\x10\x00\x01\x00\x10\x01\x10\x10\x01\x10\x10\x01\x04\x00\x00\x00\x00\x03\xc0\x0a\x00"
"\x10\x10\x04\x10\x00\x01\x00\x10\x01\x10\x10\x01\x10\x10\x01\x10\x10\x00\x00\x00\x03\xc0\x0a\x00"
"\x05\x40\x04\x25\x62\x56\x00\x22\x56\x25\x60\x02\x25\x62\x56\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; /* "\x90/bin/sh" oops just kidding */
/* *INDENT-ON* */

typedef struct {		/* dockapp info structure */
    Display *d;
    int screen;
    GdkWindow *win;
    GdkWindow *iconwin;
    GdkGC *gc;
    GdkGC *dgc;			/* drawing gc for lines and shit */
    /* there is a off-by-one error in one of the line/hline functions */
    unsigned char rgb[57 * 57];	/* main image buffer */
    unsigned char buf[57 * 57];	/* secondary image buffer */
    GdkRgbCmap cmap;		/* main colormap */
    /* The colormap is divided like this:
     *   0 .. 240  cube shades (actually only use 0..26 right now
     * 241 .. 254  misc colors (label, buttons, etc)
     * 255         background color (0x202020) */
} Dockapp;

typedef struct {		/* mouse regions */
    int enable;
    int x;
    int y;
    int width;
    int height;
} MRegion;

#define CHAR_WIDTH 5
#define CHAR_HEIGHT 7
#define PI 3.1415926535

/**** Graphics ***********************************/
void redraw_window(void);
void new_window(void);
void setupcolor(short R, short G, short B);
int add_mr(int index, int x, int y, int width, int height);
int check_mr(int x, int y);
static void copy_xpm_area(int x, int y, int w, int h, int dx, int dy);
static void putpixel(int x, int y, int c);
void line(int x1, int y1, int x2, int y2, int c);
static void hline(int x1, int x2, int y, int c);
void trcopy(void);
void triangle(int x1, int y1, int x2, int y2, int x3, int y3, int c);
void draw_cpu(int num, int x, int y);
void update_buffer(unsigned int cpu_usage);
void draw(int color);
void startup_seq();

/**** 3d specific ********************************/

void setupobj(char *filename);
void setUpAngles();
void rotate(int xang, int yang, int zang);
int normal(float p1[], float p2[], float p3[]);
int luminate(float p1[], float p2[], float p3[]);
void sortz(int nofelements);

/**** Application Management, I/O etc. ***********/

void prep_digits(void);
void print_help(void);
int loadobj(char *filename);
void mem_alloc_error(void *block);
int scan4objects(char *dir);
int next_object(void);
void die(void);

/**** System specific functions ******************/

int init_calc_cpu(void);
int calc_cpu_total(void);

/**** Global variables ***************************/

Dockapp dockapp;		/* dockapp info structure */
/* digits for the CPU load + buttons - filled in prep_digits */
static unsigned char digits[96 * 9];
MRegion mr[16];			/* mouse regions */
int xcenter, ycenter, zoff;
double cost[361], sint[361];
double acost[100];
float **matrix;
float **rmatrix;
int **planes;
int *plane_color;
int *zorder;
int *cline;
int nofcoords, noflines, nofplanes;
char *objects[1000];
int nof_objects = 0;
int show_load = 1;
int use_nice = 1;
int which_cpu = -1;
int planesORlines = 1;
char *pname;

float lum_vector[3] = { 0, 0, 100 };	/* Lightsource vector */

#ifdef FREEBSD
static kvm_t *kd;
static struct nlist nlst[] = { {"_cp_time"}, {0} };
#endif

void prep_digits(void)
{
    int i, k;
    unsigned char you, *cant, *c, me;

    cant = digits;
    c = c0de;

    for (i = 0; i < 216; i++) {
	you = *c++;
	for (k = 3; k >= 0; k--) {
	    me = (you >> 6) & 0x3;
	    *cant++ = (me) ? 240 + me : 255;
	    you <<= 2;
	}
    }
}

static void copy_xpm_area(x, y, w, h, dx, dy)
{
    int i, j;
    int spos, dpos;

    for (j = 0; j < h; j++) {
	spos = x + (96 * (y + j));
	dpos = dx + (56 * (dy + j));
	for (i = 0; i < w; i++) {
	    dockapp.buf[dpos++] = digits[spos++];
	}
    }
}

void redraw_window(void)
{
    gdk_draw_indexed_image(dockapp.win, dockapp.gc, 4, 4,
			   56, 56, GDK_RGB_DITHER_NORMAL,
			   dockapp.rgb, 56, &dockapp.cmap);
    gdk_draw_indexed_image(dockapp.iconwin, dockapp.gc, 4, 4,
			   56, 56, GDK_RGB_DITHER_NORMAL,
			   dockapp.rgb, 56, &dockapp.cmap);
}

void new_window(void)
{
    GdkWindowAttr attr;
    Window win, iconwin;
    XSizeHints sizehints;
    XWMHints wmhints;
    /* for mask */
    GdkBitmap *mask;
    unsigned char mask_data[8 * 64];
    int i;
    GdkColor bright;
    /* for that stupid shadow line */
    GdkColormap *map2;

    attr.width = 64;
    attr.height = 64;
    attr.title = "wmcube";
    attr.event_mask = GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK;
    attr.wclass = GDK_INPUT_OUTPUT;
    attr.visual = gdk_visual_get_system();
    attr.colormap = gdk_colormap_get_system();
    attr.wmclass_name = "wmcube";
    attr.wmclass_class = "wmcube";
    attr.window_type = GDK_WINDOW_TOPLEVEL;

    sizehints.flags = USSize;
    sizehints.width = 64;
    sizehints.height = 64;

    dockapp.win =
	gdk_window_new(NULL, &attr,
		       GDK_WA_TITLE | GDK_WA_WMCLASS | GDK_WA_VISUAL |
		       GDK_WA_COLORMAP);

    if (!dockapp.win) {
	fprintf(stderr, "Can't make toplevel window\n");
	exit(1);
    }

    memset(&attr, 0, sizeof(attr));

    attr.width = 64;
    attr.height = 64;
    attr.title = "wmcube";
    attr.event_mask = GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK;
    attr.wclass = GDK_INPUT_OUTPUT;
    attr.visual = gdk_visual_get_system();
    attr.colormap = gdk_colormap_get_system();
    attr.wmclass_name = "wmcube";
    attr.wmclass_class = "wmcube";
    attr.window_type = GDK_WINDOW_TOPLEVEL;

    dockapp.iconwin = gdk_window_new(dockapp.win, &attr,
				     GDK_WA_TITLE | GDK_WA_WMCLASS);

    if (!dockapp.iconwin) {
	fprintf(stderr, "Cannot make icon window\n");
	exit(1);
    }

    win = GDK_WINDOW_XWINDOW(dockapp.win);
    iconwin = GDK_WINDOW_XWINDOW(dockapp.iconwin);

    XSetWMNormalHints(GDK_WINDOW_XDISPLAY(dockapp.win), win, &sizehints);

    wmhints.initial_state = WithdrawnState;
    wmhints.icon_window = iconwin;
    wmhints.icon_x = 0;
    wmhints.icon_y = 0;
    wmhints.window_group = win;
    wmhints.flags =
	StateHint | IconWindowHint | IconPositionHint | WindowGroupHint;
    XSetWMHints(GDK_WINDOW_XDISPLAY(dockapp.win), win, &wmhints);

    /* make the mask rectangle */
    memset(mask_data, 0x00, 8 * 64);
    for (i = 3; i < 61; i++) {
	memset((mask_data + (i * 8)) + 1, 0xFF, 7);
	mask_data[i * 8] = 0xf8;
	mask_data[i * 8 + 7] = 0x1f;
    }
    mask = gdk_bitmap_create_from_data(dockapp.win, mask_data, 64, 64);

    dockapp.gc = gdk_gc_new(dockapp.win);
    dockapp.dgc = gdk_gc_new(dockapp.win);

    /* bwahaha, look at the shit we have to go through just to make a damn
     * color. fucking colormaps */
    map2 = gdk_colormap_get_system();
    gdk_color_parse("#c7c3c7", &bright);
    gdk_colormap_alloc_color(map2, &bright, False, True);
    gdk_gc_set_foreground(dockapp.dgc, &bright);

    /* combine mask with window */
    gdk_window_shape_combine_mask(dockapp.win, mask, 0, 0);
    gdk_window_shape_combine_mask(dockapp.iconwin, mask, 0, 0);

    gdk_window_show(dockapp.win);
}

int add_mr(int index, int x, int y, int width, int height)
{
    mr[index].enable = 1;
    mr[index].x = x;
    mr[index].y = y;
    mr[index].width = width;
    mr[index].height = height;
    return 0;
}

int check_mr(int x, int y)
{
    register int i;
    register int found = 0;

    for (i = 0; i < 32 && !found; i++) {
	if (mr[i].enable && x >= mr[i].x &&
	    x <= mr[i].x + mr[i].width &&
	    y >= mr[i].y && y <= mr[i].y + mr[i].height)
	    found = 1;
    }
    if (!found)
	return -1;
    return (i - 1);

}

int main(int argc, char **argv)
{
    int j, i = 0, rot_speed = 0, cpu_usage = 0, rot_step = 1;
#ifdef PRO
    int count = 2500;
#endif
    long screen_speed = 10000;	/* microseconds between screen updates (approx.) */
    long cpu_update = 490000;	/* microseconds between cpu update (approx.) */
    int loop;
    GdkEvent *event;
    GdkColor shade = { 0, 0, 0, 0 };
    char *tmp = NULL;

    char *rotdiv = { "25" };
    char *rotstep = { "1" };
    char *obj_filename = { "" };
    int rot;
    /* a little further into colormap, darker */
    int cube_color = 20;
    int c = 0;
    int invert_speed = 0;

    pname = strrchr(argv[0], '/');
    if (pname == NULL)
	pname = argv[0];

    srand((unsigned) time(NULL));
    opterr = 0;

    while ((c = getopt(argc, argv, "d:nhpbir:o:c:s:")) != -1) {
	switch (c) {
	case 'c':
	    which_cpu = atoi(optarg);
	    break;
	case 'd':
	    rotstep = optarg;
	    break;
	case 'h':
	    print_help();
	    return 1;
	case 'i':
	    invert_speed = 1;
	    break;
	case 'p':
	    show_load = 0;
	    break;
	case 'b':
	    /* this is an index into our colormap. */
	    cube_color = 0;
	    break;
	case 'r':
	    rotdiv = optarg;
	    break;
	case 'o':
	    obj_filename = optarg;
	    break;
	case 'n':
	    use_nice = 0;
	    break;
	case 's':
	    tmp = strdup(optarg);
	    break;
	case '?':
	    print_help();
	    return 1;
	default:
	    abort();
	}
    }

    /*
     * Validate that wmcube can run on this system given the parameters,
     * then setup the statistics gathering subsystem.
     */

    if (init_calc_cpu() != 0)
	die();

    /* unpack font + button images */
    prep_digits();

    /*
     * Scan directory for .wmc files and choose one randomly. If the user 
     * specified a particular file, load that one.
     */

#ifndef SOLARIS			/* scan4objects doesnt work on Solaris, load object immediatly */
    scan4objects(obj_filename);

    if (nof_objects != 0)
	next_object();
    else
#endif
	setupobj(obj_filename);

    /*
     * Various initializion stuff for the 3d-engine etc.
     */

    setUpAngles();

    rot = atoi(rotdiv);
    if ((rot >= 1) && (rot <= 100));
    else
	rot = 25;

    rot_step = atoi(rotstep);
    if (rot_step < 0)
	rot_step = -rot_step;

    if (calc_cpu_total() == -1)
	die();

    cpu_update /= screen_speed;
    loop = cpu_update;


    if (!gdk_init_check(&argc, &argv)) {
	fprintf(stderr, "GDK init failed\n");
	exit(1);
    }
    gdk_rgb_init();


    memset(dockapp.buf, 0xFF, 56 * 56);

    if (tmp) {
	if (!gdk_color_parse(tmp, &shade)) {
	    fprintf(stderr, "Incorrect color specification\n");
	    exit(1);
	}
	free(tmp);
	setupcolor(shade.red >> 8, shade.green >> 8, shade.blue >> 8);
    } else {
	setupcolor(0x20, 0xB2, 0xAE);
    }

    new_window();

    if (calc_cpu_total() == -1)
	die();

    /* index, left, top, right, bottom */
    add_mr(1, 51, 51, 6, 6);	/* + Zoom In */
    add_mr(5, 7, 51, 6, 6);	/* - Zoom Out */
    add_mr(3, 21, 51, 24, 7);	/*   Show cpu-load */
    add_mr(2, 4, 4, 56, 46);	/* Everywhere else (almost) to change object */

    /*
     * Main loop begins here
     */
    zoff = 3600;

#ifndef PRO
    while (1) {
#else
    while (count--) {
#endif
	i = (i + rot_speed + rot_step) % 360;

	rotate(i, i, i);

	memset(dockapp.rgb, 0xff, 56 * 56);
	draw(cube_color);
	trcopy();

	if (loop++ > cpu_update) {
	    loop = 0;
	    /*
	     * call calc_cpu_total to update statistics.  If some
	     * sort of bad event occurs, calc_cpu_total will return
	     * -1, and we exit.
	     */
	    if ((cpu_usage = calc_cpu_total()) == -1) {
		die();
	    }
	    rot_speed = abs(invert_speed * (100 / rot) - cpu_usage / rot);
	    /* redraw the CPU usage and update screen */
	    update_buffer(cpu_usage);
	}

	/* X Events */
	while (gdk_events_pending()) {
	    event = gdk_event_get();
	    if (event) {
		switch (event->type) {
		case GDK_EXPOSE:
		    /* redraw the shade + 3d window. This doesn't
		     * happen too often. Infact if you don't cover up
		     * the dock, only once */
		    gdk_draw_line(dockapp.win, dockapp.dgc, 60, 4, 60, 60);
		    gdk_draw_line(dockapp.iconwin, dockapp.dgc, 60, 4, 60,
				  60);
		    gdk_draw_line(dockapp.win, dockapp.dgc, 3, 60, 60, 60);
		    gdk_draw_line(dockapp.iconwin, dockapp.dgc, 3, 60, 60,
				  60);
		    redraw_window();
		    break;
		case GDK_DESTROY:
		    gdk_exit(0);
		    exit(0);
		    break;
		case GDK_BUTTON_PRESS:
		    j = check_mr(event->button.x, event->button.y);
		    switch (j) {
		    case 1:
			if (zoff > 750) {
			    copy_xpm_area(88, 0, 6, 9, 47, 46);
			    zoff -= 150;
			}
			break;
		    case 2:
			next_object();
			break;
		    case 3:
			if (show_load == 1)
			    show_load = 0;
			else
			    show_load = 1;
			ycenter = 14 - 2 * show_load;
			break;
		    case 5:
			copy_xpm_area(74, 0, 6, 9, 2, 46);
			zoff += 150;
			break;
		    }
		    break;
		default:
		    break;
		}
	    }
	}
	redraw_window();
#ifndef PRO
	usleep(screen_speed);
#endif
    }

    /*
     * Free up memory used by the object (dirty...) (also never gets here)
     */
    free(matrix);
    free(rmatrix);
    free(cline);

    return 0;
}


/**** Graphics ***********************************
 ************************************************/

void trcopy(void)
{
    unsigned int i = 56 * 56;
    while (i--)
	if (dockapp.buf[i] != 0xff)
	    dockapp.rgb[i] = dockapp.buf[i];
}

void update_buffer(unsigned int cpu_usage)
{
    memset(dockapp.buf, 0xFF, 56 * 56);
    if (show_load)
	draw_cpu(cpu_usage, 22, 46);
    copy_xpm_area(67, 0, 6, 9, 2, 46);
    copy_xpm_area(81, 0, 6, 9, 47, 46);
}

void draw(int color)
{
    int i;

    if (planesORlines) {

	sortz(nofplanes);
	for (i = 0; i < nofplanes; i++) {
	    if (normal
		(rmatrix[planes[zorder[i]][0]],
		 rmatrix[planes[zorder[i]][1]],
		 rmatrix[planes[zorder[i]][2]]) > 0) {

		triangle(xcenter + rmatrix[planes[zorder[i]][0]][0],
			 ycenter + rmatrix[planes[zorder[i]][0]][1],
			 xcenter + rmatrix[planes[zorder[i]][1]][0],
			 ycenter + rmatrix[planes[zorder[i]][1]][1],
			 xcenter + rmatrix[planes[zorder[i]][2]][0],
			 ycenter + rmatrix[planes[zorder[i]][2]][1],
			 plane_color[zorder[i]]);
	    }
	}

    } else {
	for (i = 0; i < noflines; i += 2)
	    line(xcenter + rmatrix[cline[i] - 1][0],
		 ycenter + rmatrix[cline[i] - 1][1],
		 xcenter + rmatrix[cline[i + 1] - 1][0],
		 ycenter + rmatrix[cline[i + 1] - 1][1], color);
    }
}

static void putpixel(int x, int y, int c)
{
    int pos;
    if ((x >= 0) && (x < 56) && (y >= 0) && (y <= 56)) {
	pos = x + (y * 56);
	dockapp.rgb[pos] = c;
    }
}

static void hline(int x1, int x2, int y, int c)
{
    register int i;

    if (c > 25)
	c = 25;

    if ((y >= 0) && (y <= 56)) {
	if (x1 < 0)
	    x1 = 0;
	else if (x1 > 56)
	    return;
	if (x2 > 56)
	    x2 = 56;
	else if (x2 < 0)
	    return;

	for (i = x1; i < x2; i++) {
	    register unsigned int pos;
	    pos = y * 56 + i;
	    dockapp.rgb[pos] = c;
	}
    }
}

void triangle(int x1, int y1, int x2, int y2, int x3, int y3, int c)
{				/* Draws a filled triangle */
    int k, k2, x, x_2, i, tmp1;

    int x1t, x2t;

    if (y3 < y2) {
	tmp1 = y2;
	y2 = y3;
	y3 = tmp1;
	tmp1 = x2;
	x2 = x3;
	x3 = tmp1;
    }

    if (y2 < y1) {
	tmp1 = y1;
	y1 = y2;
	y2 = tmp1;
	tmp1 = x1;
	x1 = x2;
	x2 = tmp1;
    }

    if (y3 < y2) {
	tmp1 = y2;
	y2 = y3;
	y3 = tmp1;
	tmp1 = x2;
	x2 = x3;
	x3 = tmp1;
    }

    if (y1 != y3)
	k = ((x1 - x3) << 6) / (y1 - y3);
    else
	k = (x1 - x3) << 6;

    if (y1 != y2)
	k2 = ((x1 - x2) << 6) / (y1 - y2);
    else
	k2 = (x1 - x2) << 6;

    x = x1 << 6;
    x_2 = x;
    i = y1;

    if (i != y2)
	do {
	    x += k;
	    x_2 += k2;
	    i++;

	    if ((x1t = x >> 6) > (x2t = x_2 >> 6))
		hline(x2t, x1t, i, c);
	    else
		hline(x1t, x2t, i, c);
	}
	while (i != y2);

    if (i == y3)
	return;

    if (y2 != y3)
	k2 = ((x2 - x3) << 6) / (y2 - y3);
    else
	k2 = ((x2 - x3) << 6);

    x_2 = x2 << 6;
    i = y2;
    do {
	x += k;
	x_2 += k2;
	i++;

	if ((x1t = x >> 6) > (x2t = x_2 >> 6))
	    hline(x2t, x1t, i, c);
	else
	    hline(x1t, x2t, i, c);
    }
    while (i != y3);
}

void draw_cpu(int num, int x, int y)
{
    char buf[5];
    int newx = x;
    int i;
    int c;

    if (num > 99) {
	newx -= CHAR_WIDTH;
    }

    if (num > 999) {
	newx -= CHAR_WIDTH;
    }

    snprintf(buf, 5, "%02i%%", num);
    for (i = 0; (c = buf[i]); i++) {
	if (c == '%')
	    copy_xpm_area(60, 0, 7, 9, newx, y);
	else {
	    c -= '0';
	    copy_xpm_area(c * 6, 0, 7, 9, newx, y);
	}
	newx += 6;
    }
}

void line(int x1, int y1, int x2, int y2, int c)
{
    int i, deltax, deltay, numpixels,
	d, dinc1, dinc2, x, xinc1, xinc2, y, yinc1, yinc2;

    deltax = abs(x2 - x1);
    deltay = abs(y2 - y1);
    if (deltax >= deltay) {
	numpixels = deltax + 1;
	d = (deltay << 1) - deltax;
	dinc1 = deltay << 1;
	dinc2 = (deltay - deltax) << 1;
	xinc1 = 1;
	xinc2 = 1;
	yinc1 = 0;
	yinc2 = 1;
    } else {
	numpixels = deltay + 1;
	d = (deltax << 1) - deltay;
	dinc1 = deltax << 1;
	dinc2 = (deltax - deltay) << 1;
	xinc1 = 0;
	xinc2 = 1;
	yinc1 = 1;
	yinc2 = 1;
    }
    if (x1 > x2) {
	xinc1 = -xinc1;
	xinc2 = -xinc2;
    }
    if (y1 > y2) {
	yinc1 = -yinc1;
	yinc2 = -yinc2;
    }
    x = x1;
    y = y1;
    for (i = 1; i < numpixels; i++) {
	putpixel(x, y, c);
	if (d < 0) {
	    d = d + dinc1;
	    x = x + xinc1;
	    y = y + yinc1;
	} else {
	    d = d + dinc2;
	    x = x + xinc2;
	    y = y + yinc2;
	}
    }
}


/**** 3d specific ********************************
 ************************************************/

void rotate(int xang, int yang, int zang)
{
    float tx, ty, tz;
    int i;

    for (i = 0; i < nofcoords; i++) {
	tx = cost[yang] * matrix[i][0] - sint[yang] * matrix[i][2];
	tz = sint[yang] * matrix[i][0] + cost[yang] * matrix[i][2];
	ty = cost[zang] * matrix[i][1] - sint[zang] * tx;

	rmatrix[i][0] = (cost[zang] * tx + sint[zang] * matrix[i][1]);
	rmatrix[i][1] = (sint[xang] * tz + cost[xang] * ty);
	rmatrix[i][2] = (cost[xang] * tz - sint[xang] * ty);
    }

    if (planesORlines)
	for (i = 0; i < nofplanes; i++)
	    if (normal
		(rmatrix[planes[i][0]], rmatrix[planes[i][1]],
		 rmatrix[planes[i][2]]) > 0)
		plane_color[i] =
		    luminate(rmatrix[planes[i][0]], rmatrix[planes[i][1]],
			     rmatrix[planes[i][2]]);

    for (i = 0; i < nofcoords; i++) {
	/* Perspective correcting lines... */
	rmatrix[i][0] =
	    (rmatrix[i][0] * 256) / (2 * rmatrix[i][2] - zoff) + xcenter;
	rmatrix[i][1] =
	    (rmatrix[i][1] * 256) / (2 * rmatrix[i][2] - zoff) + ycenter;
    }
}

void sortz(int nofelements)
{				/* Insertion-sort the planes in increasing z-distance */

    int i, j, k;
    float key;
    float temparr[nofelements];

    for (i = 0; i < nofelements; i++) {
	zorder[i] = i;
	temparr[i] =
	    rmatrix[planes[i][0]][2] + rmatrix[planes[i][1]][2] +
	    rmatrix[planes[i][2]][2];
    }

    for (j = 1; j < nofelements; j++) {

	key = temparr[j];
	k = zorder[j];
	i = j - 1;

	while ((i > -1) && (temparr[i] > key)) {
	    temparr[i + 1] = temparr[i];
	    zorder[i + 1] = zorder[i--];
	}

	zorder[i + 1] = k;
	temparr[i + 1] = key;
    }
}

int normal(float p1[], float p2[], float p3[])
{
    return ((p1[0] - p3[0]) * (p2[1] - p3[1]) -
	    (p2[0] - p3[0]) * (p1[1] - p3[1]));
}

int luminate(float p1[], float p2[], float p3[])
{
    double x1 = (float) (p1[0] - p3[0]), y1 = (float) (p1[1] - p3[1]), z1 =
	(float) (p1[2] - p3[2]);
    double x2 = (float) (p2[0] - p3[0]), y2 = (float) (p2[1] - p3[1]), z2 =
	(float) (p2[2] - p3[2]);
    double nx = y1 * z2 - y2 * z1, ny = -(x1 * z2 - x2 * z1), nz =
	x1 * y2 - y1 * x2;

    return (int) (53 * (acost[(int)
			      (50 +
			       50 * (nx * lum_vector[0] +
				     ny * lum_vector[1] +
				     nz * lum_vector[2]) / (sqrt(nx * nx +
								 ny * ny +
								 nz * nz) *
							    sqrt(lum_vector
								 [0] *
								 lum_vector
								 [0] +
								 lum_vector
								 [1] *
								 lum_vector
								 [1] +
								 lum_vector
								 [2] *
								 lum_vector
								 [2])))] /
			PI));

    /* Do I smell optimization? :-) */
}

void setUpAngles(void)
{
    int i;
    for (i = 0; i < 361; i++) {
	cost[i] = cos((double) i * (2 * PI / (double) 360));
	sint[i] = sin((double) i * (2 * PI / (double) 360));
    }

    for (i = 0; i < 100; i++)
	acost[i] = acos((double) (-50 + i) / 50);
}

void setupcolor(short R, short G, short B)
{
    int i;
    int logi;
    unsigned char r, g, b;

    for (i = 0; i < 26; i++) {
	/* feel free to come up with a better formula here.
	 * I chose log due to lack of any better ideas. */
	logi = i * log(i + 1);
	r = MAX(0, R - logi);			/* R */
	g = MAX(0, G - logi);			/* G */
	b = MAX(0, B - logi);			/* B */
	dockapp.cmap.colors[i] = r << 16 | g << 8 | b;
    }

    /* setup misc colors */
    dockapp.cmap.colors[255] = 0x202020;	/* background */
    dockapp.cmap.colors[241] = 0x00b1ad;	/* load digit #1 */
    dockapp.cmap.colors[242] = 0x007571;	/* load digit #2 */
    dockapp.cmap.colors[243] = 0x004941;	/* icon dark */
}

void setupobj(char *filename)
{
    int i, j = 0;
    int biggest = 0;
    float scale = 1;

    xcenter = 14;
    ycenter = 14 - 2 * show_load;

    if (strcmp(filename, "") != 0)
	loadobj(filename);
    else {
	nofcoords = 8;
	noflines = 24;
	nofplanes = 12;
	planesORlines = 1;

	matrix = (float **) malloc(nofcoords * sizeof(float *));
	mem_alloc_error(matrix);
	planes = (int **) malloc(nofplanes * sizeof(int *));
	mem_alloc_error(planes);
	plane_color = (int *) malloc(nofplanes * sizeof(int));
	mem_alloc_error(plane_color);
	zorder = (int *) malloc(nofplanes * sizeof(int));
	mem_alloc_error(zorder);

	for (i = 0; i < nofplanes; i++)
	    zorder[i] = i;

	for (i = 0; i < nofcoords; i++) {
	    matrix[i] = (float *) malloc(3 * sizeof(float));
	    mem_alloc_error(matrix[i]);
	}

	for (i = 0; i < nofplanes; i++) {
	    planes[i] = (int *) malloc(3 * sizeof(int));
	    mem_alloc_error(planes[i]);
	}

	cline = (int *) malloc((noflines + 1) * sizeof(int));
	mem_alloc_error(cline);

	matrix[0][0] = -180;
	matrix[0][1] = -180;
	matrix[0][2] = 180;	/* 0 */
	matrix[1][0] = 180;
	matrix[1][1] = -180;
	matrix[1][2] = 180;	/* 1 */
	matrix[2][0] = 180;
	matrix[2][1] = 180;
	matrix[2][2] = 180;	/* 2 */
	matrix[3][0] = -180;
	matrix[3][1] = 180;
	matrix[3][2] = 180;	/* 3 */
	matrix[4][0] = -180;
	matrix[4][1] = -180;
	matrix[4][2] = -180;	/* 4 */
	matrix[5][0] = 180;
	matrix[5][1] = -180;
	matrix[5][2] = -180;	/* 5 */
	matrix[6][0] = 180;
	matrix[6][1] = 180;
	matrix[6][2] = -180;	/* 6 */
	matrix[7][0] = -180;
	matrix[7][1] = 180;
	matrix[7][2] = -180;	/* 7 */

	cline[0] = 1;
	cline[1] = 2;
	cline[2] = 2;
	cline[3] = 3;
	cline[4] = 3;
	cline[5] = 4;
	cline[6] = 4;
	cline[7] = 1;
	cline[8] = 5;
	cline[9] = 6;
	cline[10] = 6;
	cline[11] = 7;
	cline[12] = 7;
	cline[13] = 8;
	cline[14] = 8;
	cline[15] = 5;
	cline[16] = 1;
	cline[17] = 5;
	cline[18] = 2;
	cline[19] = 6;
	cline[20] = 3;
	cline[21] = 7;
	cline[22] = 4;
	cline[23] = 8;

	planes[0][0] = 0;
	planes[0][1] = 1;
	planes[0][2] = 3;
	planes[1][0] = 1;
	planes[1][1] = 2;
	planes[1][2] = 3;
	planes[2][0] = 1;
	planes[2][1] = 5;
	planes[2][2] = 6;
	planes[3][0] = 1;
	planes[3][1] = 6;
	planes[3][2] = 2;

	planes[4][0] = 4;
	planes[4][1] = 0;
	planes[4][2] = 3;
	planes[5][0] = 4;
	planes[5][1] = 3;
	planes[5][2] = 7;
	planes[6][0] = 3;
	planes[6][1] = 2;
	planes[6][2] = 7;
	planes[7][0] = 7;
	planes[7][1] = 2;
	planes[7][2] = 6;

	planes[8][0] = 4;
	planes[8][1] = 1;
	planes[8][2] = 0;
	planes[9][0] = 4;
	planes[9][1] = 5;
	planes[9][2] = 1;
	planes[10][0] = 5;
	planes[10][1] = 4;
	planes[10][2] = 7;
	planes[11][0] = 5;
	planes[11][1] = 7;
	planes[11][2] = 6;
    }

    rmatrix = (float **) realloc(rmatrix, nofcoords * sizeof(float *));
    mem_alloc_error(rmatrix);
    for (i = 0; i < nofcoords; i++) {
	rmatrix[i] = (float *) malloc(3 * sizeof(float));
	mem_alloc_error(rmatrix[i]);
    }

    /*
     * Find the longest discance between all coordinates relative to the origin
     */

    for (i = 0; i < nofcoords; i++) {
	j = (int)
	    sqrt((pow(matrix[i][0], 2) + pow(matrix[i][1], 2) +
		  pow(matrix[i][2], 2)));
	if (j > biggest)
	    biggest = j;
    }

    /*
     * Scale every coordinate using the calculated factor
     */

    scale = 280 / (float) biggest;

    for (i = 0; i < nofcoords; i++) {
	matrix[i][0] *= scale;
	matrix[i][1] *= scale;
	matrix[i][2] *= scale;
    }
}


/**** Application Management, I/O etc. ***********
 ************************************************/

void print_help(void)
{
    printf("\nwmCube %s (%s)\n\n", WMCUBE_VERSION, REV_DATE);

#ifndef SOLARIS
    printf
	("  -o <filename or directory>: load external 3d-object(s).\n\n");
#else
    printf("  -o <filename>: load external 3d-object.\n\n");
#endif

    printf
	("  -d x: rotate x degrees/step when the cpu is idle. (default 1)\n");
    printf
	("  -r x: rotate 1 degree faster every x percent of cpu-usage. (default 25)\n");

#ifdef LINUX
    printf
	("  -c x: which cpu (0,1,2..) to monitor. (default average over all)\n");
    printf("  -n  : exclude \"nice\" processes. (default OFF)\n");
#endif

#ifdef SOLARIS
    printf
	("  -c x: which cpu (0,1,2..) to monitor. (default average over all)\n");
#endif

#ifdef FREEBSD
    printf("  -n  : exclude \"nice\" processes. (default OFF)\n");
#endif

#ifdef NETBSD
    printf("  -n  : exclude \"nice\" processes. (default OFF)\n");
#endif

#ifdef OPENBSD
    printf("  -n  : exclude \"nice\" processes. (default OFF)\n");
#endif

    printf("  -b  : draw the cube in a brighter color. (default OFF)\n");
    printf("  -s x: parse color 'x' and use to draw 3D shaded objects\n");
    printf("  -i  : invert cube speed. (default OFF)\n");
    printf("  -p  : do not display cpu-load (default OFF)\n");
    printf("  -h  : display this helptext.\n\n");
}

void die(void)
{
    fprintf(stderr, "%s: exiting", pname);
    exit(1);
}

#ifndef SOLARIS			/* scan4objects doesnt work on Solaris because of alphasort */
int scan4objects(char *dir)
{
    struct dirent **names;
    int n;

    n = scandir(dir, &names, 0, alphasort);

    while (n-- > 0)
	if (strstr(names[n]->d_name, ".wmc") != NULL) {
	    objects[nof_objects] =
		(char *) malloc(strlen(dir) + strlen(names[n]->d_name) +
				2);
	    strcpy(objects[nof_objects], dir);
	    if (dir[strlen(dir)] != '/')
		strcat(objects[nof_objects], "/");
	    strcat(objects[nof_objects++], names[n]->d_name);
	}

    return nof_objects;
}

#endif

int next_object(void)
{
    if (nof_objects == 0)
	return -1;
    setupobj(objects[rand() % (nof_objects)]);

    return 0;
}


int loadobj(char *filename)
{

    FILE *fp;
    char tmp[64] = { "" };
    int i = 0, counter = 1;

    /* printf("\nLoading file %s...",filename); fflush(stdout); */

    if ((fp = fopen(filename, "rt")) == NULL) {
	printf("\nERROR: wmCube object-file not found (%s).\n\n",
	       filename);
	exit(0);
    }

    fscanf(fp, "%63s", tmp);

    if (strcmp(tmp, "WMCUBE_COORDINATES") != 0) {
	printf
	    ("\nError in objectfile: it must start with WMCUBE_COORDINATES\n\n");
	fclose(fp);
	exit(0);
    }

    fscanf(fp, "%63s", tmp);
    counter = atoi(tmp);

    while ((strcmp(tmp, "WMCUBE_LINES") != 0)
	   && (strcmp(tmp, "WMCUBE_PLANES") != 0)) {

	matrix = (float **) realloc(matrix, (i + 1) * sizeof(float *));
	mem_alloc_error(matrix);
	matrix[i] = (float *) malloc(3 * sizeof(float));
	mem_alloc_error(matrix[i]);
	fscanf(fp, "%f %f %f", &matrix[i][0], &matrix[i][1],
	       &matrix[i][2]);
	/* printf("\n%d: %f %f %f",atoi(tmp), matrix[i][0],matrix[i][1],matrix[i][2]); */

	if (atoi(tmp) != (++i)) {

	    printf("\nError in objectfile (WMCUBE_COORDINATES section):\n"
		   "the coordinates must be listed in order 1..n\n\n");
	    fclose(fp);
	    exit(0);
	}
	fscanf(fp, "%63s", tmp);

	if (feof(fp)) {
	    printf
		("\nError in objectfile: you must have a section WMCUBE_LINES or WMCUBE_PLANES\n\n");
	    fclose(fp);
	    exit(0);
	}
    }

    nofcoords = i;
    i = 0;

    if (strcmp(tmp, "WMCUBE_LINES") == 0) {

	planesORlines = 0;
	while (1) {

	    cline = (int *) realloc(cline, (i + 2) * sizeof(int));
	    mem_alloc_error(cline);
	    fscanf(fp, "%d %d", &cline[i++], &cline[i++]);
	    /* printf("\n%d %d",cline[i-2],cline[i-1]); */
	    if (feof(fp))
		break;

	    if (cline[i - 2] > nofcoords || cline[i - 1] > nofcoords) {
		printf("\nError in objectfile (WMCUBE_LINES section):\n"
		       "coordinates %d or/and %d doesnt exist\n\n",
		       cline[i - 2], cline[i - 1]);
		fclose(fp);
		exit(0);
	    }
	}
	noflines = i - 2;
    } else if (strcmp(tmp, "WMCUBE_PLANES") == 0) {

	planesORlines = 1;
	while (1) {
	    planes = (int **) realloc(planes, (i + 1) * sizeof(int *));
	    mem_alloc_error(planes);
	    planes[i] = (int *) malloc(3 * sizeof(int));
	    mem_alloc_error(planes[i]);
	    fscanf(fp, "%d %d %d", &planes[i][0], &planes[i][1],
		   &planes[i][2]);
	    /* printf("\n%d: %d %d %d",i,planes[i][0],planes[i][1],planes[i][2]); */

	    planes[i][0]--;
	    planes[i][1]--;
	    planes[i][2]--;
	    /* printf("\n%d: %d %d %d\n",i,planes[i][0],planes[i][1],planes[i][2]); */

	    if (feof(fp))
		break;

	    if (planes[i][0] > nofcoords || planes[i][1] > nofcoords
		|| planes[i][2] > nofcoords) {
		printf("\nError in objectfile (WMCUBE_PLANES section):\n"
		       "coordinates %d or/and %d or/and %d doesnt exist\n\n",
		       planes[i][0], planes[i][1], planes[i][2]);
		fclose(fp);
		exit(0);
	    }
	    i++;
	}
	nofplanes = i;
	plane_color = (int *) malloc(nofplanes * sizeof(int));
	mem_alloc_error(plane_color);
	zorder = (int *) malloc(nofplanes * sizeof(int));
	mem_alloc_error(zorder);
	for (i = 0; i < nofplanes; i++)
	    zorder[i] = i;

    } else {
	printf
	    ("\nError in objectfile: you must have a section WMCUBE_LINES or WMCUBE_PLANES\n\n");
	fclose(fp);
	exit(0);
    }

    fclose(fp);
    return 1;
}

void mem_alloc_error(void *block)
{
    if (block == NULL) {
	printf("\nError allocating memory!\n\n");
	exit(1);
    }
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
 * Begin System Specific Code.  If you wish to port wmcube to a new platform,
 * you'll need to implement the following operations:
 *
 * int init_calc_cpu();
 * 	Perform feature tests to determine whether wmcube can run, and set up
 * 	any files/data structures/etc. to gather statistics.
 *
 * int calc_cpu_total();
 * 	return an integer reflecting the current CPU load
 */

#if defined LINUX

/*
 * init_calc_cpu doesn't have much to do on Linux, but it can check to see if
 * /proc/stat is available; if the user selected to monitor a particular CPU,
 * it can check it's existence.
 */
int init_calc_cpu()
{
    FILE *fp;
    int i;
    char cpuid[6];
    char check_cpu[6];

    snprintf(check_cpu, 6, "cpu%d", which_cpu);

    if ((fp = fopen("/proc/stat", "rb")) == NULL) {
	perror("/proc/stat required for this system");
	return -1;
    }

    if (which_cpu == -1)
	return 0;

    for (i = -2; i < which_cpu; i++) {
	fscanf(fp, "%5s", cpuid);
    }

    if (strcmp(check_cpu, cpuid) != 0) {
	fprintf(stderr, "ERROR: could not read cpu-load on %s. Are you "
		"sure you have an SMP system?\n", check_cpu);
	return -1;
    }
    return (0);
}

int calc_cpu_total()
{
    int total, used, t = 0, i;
    static int previous_total = 0, previous_used = 0;
    char cpuid[6];
    int cpu, nice, system, idle;
    FILE *fp;

    fp = fopen("/proc/stat", "rt");

    for (i = -2; i < which_cpu; i++) {
	fscanf(fp, "%5s %d %d %d %d", cpuid, &cpu, &nice, &system, &idle);
    }

    fclose(fp);

    used = cpu + system + use_nice * nice;
    total = used + idle + (1 - use_nice) * nice;

    t = 100 * (double) (used - previous_used) / (double) (total -
							  previous_total);
    previous_total = total;
    previous_used = used;

    return t;
}

#elif defined SOLARIS
#include <sys/types.h>
#include <sys/sysinfo.h>
#include <kstat.h>

static kstat_ctl_t *kc;
static kstat_t **cpu_ksp_list;
static kstat_t *the_cpu;
static int ncpus;

/*
 * The biggest subtlety of the Solaris port is that init_calc_cpu can be called
 * after the initial program setup.  This occurs when a 'kstat state change'
 * occurs.  Usually this means that a CPU has been taken on or off-line using
 * the psradm command.  Another possibility is that on server systems, a new
 * CPU might have been hot-added to a running system.
 *
 * As a result, init_calc_cpu frees any resources it might have setup if needed,
 * and reinitializes everything.
 */
int init_calc_cpu()
{
    kstat_t *ksp;
    int i = 0;

    if (kc == NULL) {
	if ((kc = kstat_open()) == NULL) {
	    fprintf(stderr, "wmcube: can't open /dev/kstat\n");
	    return -1;
	}
    }

    if (which_cpu != -1) {
	/*
	 * User selected to monitor a particular CPU.  find it...
	 */
	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
	    if ((strcmp(ksp->ks_module, "cpu_stat") == 0) &&
		(ksp->ks_instance == which_cpu)) {
		the_cpu = ksp;
		break;
	    }
	}
	if (the_cpu == NULL) {
	    fprintf(stderr, "CPU %d not found\n", which_cpu);
	    return -1;
	}
    } else {
	/*
	 * User selected to monitor all CPUs.  First, count them.
	 */
	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
	    if (strcmp(ksp->ks_module, "cpu_stat") == 0)
		i++;
	}

	if (cpu_ksp_list) {
	    free(cpu_ksp_list);
	}
	cpu_ksp_list = (kstat_t **) calloc(i * sizeof(kstat_t *), 1);
	ncpus = i;

	/*
	 * stash the ksp for each CPU.
	 */
	i = 0;
	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
	    if (strcmp(ksp->ks_module, "cpu_stat") == 0) {
		cpu_ksp_list[i] = ksp;
		i++;
	    }
	}
    }
    return 0;
}

int calc_cpu_total()
{
    int i;
    cpu_stat_t stat;
    static int previous_total = 0, previous_used = 0;
    int used, total, t, user = 0, wait = 0, kern = 0, idle = 0;

    /*
     * Read each cpu's data.  If the kstat chain has changed (a state change
     * has happened, maybe a new cpu was added to the system or one went
     * away), then reinitialize everything with init_calc_cpu().  Finally,
     * recursively call calc_cpu_total.
     *
     * We'll need to do a little better than this in the future, since we
     * could recurse too much in the pathological case here.
     */
    if (which_cpu == -1) {
	for (i = 0; i < ncpus; i++) {
	    if (kstat_read(kc, cpu_ksp_list[i], (void *) &stat) == -1) {
		if (init_calc_cpu() != 0) {
		    fprintf(stderr, "failed to "
			    "reinitialize following state " "change\n");
		    return (-1);
		}
		return (calc_cpu_total());
	    }
	    user += stat.cpu_sysinfo.cpu[CPU_USER];	/* user */
	    wait += stat.cpu_sysinfo.cpu[CPU_WAIT];	/* io wait */
	    kern += stat.cpu_sysinfo.cpu[CPU_KERNEL];	/* sys */
	    idle += stat.cpu_sysinfo.cpu[CPU_IDLE];	/*idle("free") */
	}
    } else {
	if (kstat_read(kc, the_cpu, (void *) &stat) == -1) {
	    if (init_calc_cpu() != 0) {
		fprintf(stderr, "failed to reinitialize "
			"following state change\n");
		return (-1);
	    }
	    return (calc_cpu_total());
	}
	user += stat.cpu_sysinfo.cpu[CPU_USER];	/* user */
	wait += stat.cpu_sysinfo.cpu[CPU_WAIT];	/* io wait */
	kern += stat.cpu_sysinfo.cpu[CPU_KERNEL];	/* sys */
	idle += stat.cpu_sysinfo.cpu[CPU_IDLE];	/* idle("free") */
    }

    used = user + wait + kern;
    total = used + idle;
    t = 100 * (double) (used - previous_used) /
	(double) (total - previous_total);
    previous_total = total;
    previous_used = used;
    return (t);
}

#elif defined FREEBSD
#include <nlist.h>
#include <fcntl.h>
#include <sys/dkstat.h>

int init_calc_cpu()
{

    if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "kvm_open")) == NULL) {
	printf("\nError: unable to open kvm\n\n");
	exit(0);
    }
    kvm_nlist(kd, nlst);
    if (nlst[0].n_type == 0) {
	printf("\nError: unable to get nlist\n\n");
	exit(1);
    }

    /* drop setgid & setuid (hi GOBBLES, who the fuck are you? */
    seteuid(getuid());
    setegid(getgid());

    if (geteuid() != getuid() || getegid() != getgid()) {
	printf("Unable to drop privileges\n");
	exit(1);
    }

    return 0;
}

int calc_cpu_total()
{
    int total, used, t = 0;
    static int previous_total = 0, previous_used = 0;
    int cpu, nice, system, idle;
    unsigned long int cpu_time[CPUSTATES];

    if (kvm_read(kd, nlst[0].n_value, &cpu_time, sizeof(cpu_time))
	!= sizeof(cpu_time)) {
	printf("\nError reading kvm\n\n");
	exit(0);
    }

    cpu = cpu_time[CP_USER];
    nice = cpu_time[CP_NICE];
    system = cpu_time[CP_SYS];
    idle = cpu_time[CP_IDLE];

    used = cpu + system + use_nice * nice;
    total = used + idle + (1 - use_nice) * nice;

    t = 100 * (double) (used - previous_used) / (double) (total -
							  previous_total);
    previous_total = total;
    previous_used = used;

    return t;
}

#elif defined OPENBSD
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/dkstat.h>

int init_calc_cpu()
{
    return 0;
}

int calc_cpu_total()
{
    int total, used, t=0;
    static int previous_total = 0, previous_used = 0;
    int cpu,nice,system,idle;
    unsigned long int cpu_time[CPUSTATES];

    int mib[2];
    size_t size;

    mib[0] = CTL_KERN;
    mib[1] = KERN_CPTIME;
    size = sizeof (cpu_time);

    if (sysctl(mib, 2, &cpu_time, &size, NULL, 0) < 0)
	return 0;

    cpu = cpu_time[CP_USER];
    nice = cpu_time[CP_NICE];
    system = cpu_time[CP_SYS];
    idle = cpu_time[CP_IDLE];

    used = cpu + system + use_nice*nice;
    total = used + idle + (1-use_nice)*nice;

    t = 100 * (double)(used - previous_used) / (double)(total - previous_total);
    previous_total = total;
    previous_used = used;

    return t;
}


#elif defined NETBSD		/* END OPENBSD */
#include <sys/sched.h>
#include <sys/sysctl.h>

int init_calc_cpu()
{
    return 0;
}

int calc_cpu_total()
{
    static u_int64_t last_cp_time[CPUSTATES] = { 0, 0, 0, 0, 0 };
    u_int64_t curr_cp_time[CPUSTATES];
    u_int64_t total_time = 0, idle_time = 0;
    int mib[2];
    int i;
    size_t ssize;
    const int IDLE_TIME = 4;
    const int NICE_TIME = 1;

    ssize = sizeof(curr_cp_time);
    mib[0] = CTL_KERN;
    mib[1] = KERN_CP_TIME;
    if (sysctl(mib, 2, curr_cp_time, &ssize, NULL, 0)) {
	fprintf(stderr, "wmcube: unable to read CP_TIME from sysctl()\n");
	exit(0);
    }
    if (!use_nice)
	curr_cp_time[NICE_TIME] = 0;

    /* NetBSD gives 5 CPUSTATES - 
     * User, Nice, System, Interrupt, Idle 
     */
    idle_time = curr_cp_time[IDLE_TIME] - last_cp_time[IDLE_TIME];
    for (i = 0; i < CPUSTATES; i++) {
	total_time += (curr_cp_time[i] - last_cp_time[i]);
	last_cp_time[i] = curr_cp_time[i];
    }

    /* Calculate the % CPU usage as the User+Nice+System+Interrupt/Total
     * for the interval
     */
    return (100 * (int) (total_time - idle_time) / total_time);

}

#else				/* END NETBSD */

/*
 * This is a stub which will compile for platforms other than LINUX or SOLARIS.
 * Use these to start your port to a new platform.
 */
int init_calc_cpu()
{
    return 0;
}

int calc_cpu_total()
{
    return 0;
}

#endif				/* OS SPECIFIC CODE */


syntax highlighted by Code2HTML, v. 0.9.1