/*
    lpanel.c: main source file of lpanel
    Copyright (C) 2003  Nicolas Even <nicolas@nicnet.ath.cx>

    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.
*/

/********************************************************
 ** F***ing Small Panel 0.7 Copyright (c) 2000-2001 By **
 ** Peter Zelezny <zed@linuxpower.org>                 **
 ** See file COPYING for license details.              **
 ********************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

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

#ifdef HAVE_XPM
#include <X11/xpm.h>
#include "../icon.xpm"
#endif

#include "lpanel.h"
#include "drawing.h"

Window root_win;
Pixmap generic_icon;
Pixmap generic_mask;
GC fore_gc;
XFontStruct *xfs;
int scr_screen;
int scr_depth;
int scr_width;
int scr_height;
unsigned short cols[] = {
    0xd75c, 0xd75c, 0xd75c,	/* 0. light gray */
    0xbefb, 0xbaea, 0xbefb,	/* 1. mid gray */
    0xaefb, 0xaaea, 0xaefb,	/* 2. dark gray */
    0xefbe, 0xefbe, 0xefbe,	/* 3. white */
    0x8617, 0x8207, 0x8617,	/* 4. darkest gray */
    0x0000, 0x0000, 0x0000	/* 5. black */
};

#define PALETTE_COUNT (sizeof (cols) / sizeof (cols[0]) / 3)

extern unsigned long palette[PALETTE_COUNT];

char *atom_names[] = {
    "KWM_WIN_ICON",
    "_MOTIF_WM_HINTS",
    "_WIN_WORKSPACE",
    "_WIN_HINTS",
    "_WIN_LAYER",
    "_NET_CLIENT_LIST",
    "_WIN_CLIENT_LIST",
    "_WIN_WORKSPACE_COUNT",
    "_WIN_STATE",
    "WM_STATE"
};

#define ATOM_COUNT (sizeof (atom_names) / sizeof (atom_names[0]))

Atom atoms[ATOM_COUNT];

#define atom_KWM_WIN_ICON atoms[0]
#define atom__MOTIF_WM_HINTS atoms[1]
#define atom__WIN_WORKSPACE atoms[2]
#define atom__WIN_HINTS atoms[3]
#define atom__WIN_LAYER atoms[4]
#define atom__NET_CLIENT_LIST atoms[5]
#define atom__WIN_CLIENT_LIST atoms[6]
#define atom__WIN_WORKSPACE_COUNT atoms[7]
#define atom__WIN_STATE atoms[8]
#define atom_WM_STATE atoms[9]


void *get_prop_data(Window win, Atom prop, Atom type, int *items,
		    Display * dd)
{
    Atom type_ret;
    int format_ret;
    unsigned long items_ret;
    unsigned long after_ret;
    unsigned char *prop_data;

    prop_data = 0;

    XGetWindowProperty(dd, win, prop, 0, 0x7fffffff, False,
		       type, &type_ret, &format_ret, &items_ret,
		       &after_ret, &prop_data);
    if (items)
	*items = items_ret;

    return prop_data;
}



void get_task_hinticon(task * tk, Display * dd)
{
    XWMHints *hin;

    tk->icon = None;
    tk->mask = None;

    hin = (XWMHints *) get_prop_data(tk->win, XA_WM_HINTS, XA_WM_HINTS,
				     0, dd);
    if (hin) {
	if ((hin->flags & IconPixmapHint)) {
	    if ((hin->flags & IconMaskHint)) {
		tk->mask = hin->icon_mask;
	    }

	    tk->icon = hin->icon_pixmap;
	    tk->icon_copied = 1;
	    scale_icon(tk, dd);
	}
	XFree(hin);
    }

    if (tk->icon == None) {
	tk->icon = generic_icon;
	tk->mask = generic_mask;
    }
}

void get_task_kdeicon(task * tk, Display * dd)
{
    unsigned long *data;

    data = get_prop_data(tk->win, atom_KWM_WIN_ICON, atom_KWM_WIN_ICON,
			 0, dd);
    if (data) {
	tk->icon = data[0];
	tk->mask = data[1];
	XFree(data);
    }
}

int find_desktop(Window win, Display * dd)
{
    int desk = 0;
    unsigned long *data;

    data = get_prop_data(win, atom__WIN_WORKSPACE, XA_CARDINAL, 0, dd);
    if (data) {
	desk = *data;
	XFree(data);
    }
    return desk;
}

int is_hidden(Window win, Display * dd)
{
    unsigned long *data;
    int ret = 0;

    data = get_prop_data(win, atom__WIN_HINTS, XA_CARDINAL, 0, dd);
    if (data) {
	if ((*data) & WIN_HINTS_SKIP_TASKBAR)
	    ret = 1;
	XFree(data);
    }

    return ret;
}

int is_iconified(Window win, Display * dd)
{
    unsigned long *data;
    int ret = 0;

    data = get_prop_data(win, atom_WM_STATE, atom_WM_STATE, 0, dd);
    if (data) {
	if (data[0] == IconicState)
	    ret = 1;
	XFree(data);
    }
    return ret;
}

void add_task(taskbar * tb, Window win, int focus)
{
    task *tk, *list;

    /* is this window on a different desktop? */
    if (tb->my_desktop != find_desktop(win, tb->dd) || is_hidden
	(win, tb->dd))
	return;

    tk = calloc(1, sizeof(task));
    tk->win = win;
    tk->focused = focus;
    tk->name = get_prop_data(win, XA_WM_NAME, XA_STRING, 0, tb->dd);
    tk->iconified = is_iconified(win, tb->dd);

    get_task_kdeicon(tk, tb->dd);
    if (tk->icon == None)
	get_task_hinticon(tk, tb->dd);

    XSelectInput(tb->dd, win, PropertyChangeMask | FocusChangeMask |
		 StructureNotifyMask);

    /* now append it to our linked list */
    tb->num_tasks++;

    list = tb->task_list;
    if (!list) {
	tb->task_list = tk;
	return;
    }
    while (1) {
	if (!list->next) {
	    list->next = tk;
	    return;
	}
	list = list->next;
    }
}

void gui_sync(Display * dd)
{
    XSync(dd, False);
}

void set_prop(Window win, Atom at, long val, Display * dd)
{
    XChangeProperty(dd, win, at, XA_CARDINAL, 32,
		    PropModeReplace, (unsigned char *) &val, 1);
}

taskbar *gui_create_taskbar(arg, dd)
args arg;
Display *dd;
{
    taskbar *tb;
    Window win;
    MWMHints mwm;
    XSizeHints size_hints;
    XWMHints wmhints;
    XSetWindowAttributes att;

    att.background_pixel = palette[0];
    att.event_mask = ButtonPressMask | ExposureMask;

    win = XCreateWindow(
			   /* display */ dd,
			   /* parent  */ root_win,
			   /* x       */ 0,
			   /* y       */
			   arg.wintop /* scr_height - WINHEIGHT */ ,
			   /* width   */ arg.winwidth,
			   /* height  */ arg.winheight,
			   /* border  */ 0,
			   /* depth   */ CopyFromParent,
			   /* class   */ InputOutput,
			   /* visual  */ CopyFromParent,
			   /*value mask */ CWBackPixel | CWEventMask,
			   /* attribs */ &att);

    /* don't let any windows cover fspanel */
    set_prop(win, atom__WIN_LAYER, 10, dd);	/* WIN_LAYER_ABOVE_DOCK */

    set_prop(win, atom__WIN_STATE, WIN_STATE_STICKY |
	     WIN_STATE_FIXED_POSITION, dd);

    set_prop(win, atom__WIN_HINTS, WIN_HINTS_SKIP_FOCUS | WIN_HINTS_SKIP_WINLIST | WIN_HINTS_SKIP_TASKBAR	|
														   WIN_HINTS_DO_NOT_COVER  , dd);

    /* borderless motif hint */
    memset(&mwm, 0, sizeof(mwm));
    mwm.flags = MWM_HINTS_DECORATIONS;
    XChangeProperty(dd, win, atom__MOTIF_WM_HINTS,
		    atom__MOTIF_WM_HINTS, 32, PropModeReplace,
		    (unsigned char *) &mwm, sizeof(MWMHints) / 4);

    /* make sure the WM obays our window position */
    size_hints.flags = PPosition;
    /*XSetWMNormalHints (dd, win, &size_hints); */
    XChangeProperty(dd, win, XA_WM_NORMAL_HINTS,
		    XA_WM_SIZE_HINTS, 32, PropModeReplace,
		    (unsigned char *) &size_hints, sizeof(XSizeHints) / 4);

    /* make our window unfocusable */
    wmhints.flags = InputHint;
    wmhints.input = 0;
    /*XSetWMHints (dd, win, &wmhints); */
    XChangeProperty(dd, win, XA_WM_HINTS,
		    XA_WM_HINTS, 32, PropModeReplace,
		    (unsigned char *) &wmhints, sizeof(XWMHints) / 4);

    XMapWindow(dd, win);

    tb = calloc(1, sizeof(taskbar));
    tb->win = win;

    tb->width = arg.winwidth;
    tb->height = arg.winheight;
    tb->rowheight = arg.rowheight;
    tb->dd = dd;

    return tb;
}

void gui_init(Display * dd)
{
    XGCValues gcv;
    XColor xcl;
    int i, j;
    char *fontname;

    i = j = 0;
    do {
	xcl.red = cols[i];
	i++;
	xcl.green = cols[i];
	i++;
	xcl.blue = cols[i];
	i++;
	XAllocColor(dd, DefaultColormap(dd, scr_screen), &xcl);
	palette[j] = xcl.pixel;
	j++;
    }
    while (j < PALETTE_COUNT);

    fontname = FONT_NAME;
    do {
	xfs = XLoadQueryFont(dd, fontname);
	fontname = "fixed";
    }
    while (!xfs);

    gcv.font = xfs->fid;
    gcv.graphics_exposures = False;
    fore_gc = XCreateGC(dd, root_win, GCFont | GCGraphicsExposures, &gcv);

#ifdef HAVE_XPM
    XpmCreatePixmapFromData(dd, root_win, icon_xpm, &generic_icon,
			    &generic_mask, NULL);
#else
    generic_icon = 0;
#endif
}


void gui_draw_task(taskbar * tb, task * tk)
{
    int len;
    int x = tk->pos_x;
    int y = tk->pos_y;
    int taskw = tk->width;
    int text_y;

    if (!tk->name)
	return;			/* I don't get what this is */

    gui_draw_vline(tb, x, y / tb->rowheight);
    /*REPL*/
	/*set_foreground (3); *//* it's already 3 from gui_draw_vline() */
#if 1				/* When disabled, funny marks appear on top and bottom TOFIX */
	/* Draw top border */
	draw_line(tb, x + 1, y, x + taskw, y);

    /* Draw bottom border */
    set_foreground(1, tb->dd);
    draw_line(tb, x + 1, y + tb->rowheight - 1, x + taskw,
	      y + tb->rowheight - 1);
#endif
    /* TODO: implement functions to draw buttons or use a toolkit. */
    if (tk->focused) {
	x++;
	set_foreground(1, tb->dd);	/* mid gray */
	fill_rect(tb, x + 3, y + 3, taskw - 5, tb->rowheight - 6);
	set_foreground(3, tb->dd);	/* white */
	draw_line(tb, x + 2, y + tb->rowheight - 2, x + taskw - 2,
		  y + tb->rowheight - 2);
	draw_line(tb, x + taskw - 2, y + 2, x + taskw - 2,
		  y + tb->rowheight - 2);
	set_foreground(0, tb->dd);
	draw_line(tb, x + 1, y + 2, x + 1, y + tb->rowheight - 2);
	set_foreground(4, tb->dd);	/* darkest gray */
	draw_line(tb, x + 2, y + 2, x + taskw - 2, y + 2);
	draw_line(tb, x + 2, y + 2, x + 2, y + tb->rowheight - 3);
    } else {
	set_foreground(0, tb->dd);	/* mid gray */
	fill_rect(tb, x + 2, y + 1, taskw - 1, tb->rowheight - 2);
    }

    {
	int text_x = x + TEXTPAD * 2 + ICONWIDTH;

	/* check how many chars can fit */
	len = strlen(tk->name);
	while (XTextWidth(xfs, tk->name, len) >= taskw - (text_x - x) - 2
	       && len > 0)
	    len--;
/* Added this, make text_y local. FIXME*/
	text_y = y + xfs->ascent + ((tb->rowheight - xfs->ascent) / 2);
	if (tk->iconified) {
	    /* draw task's name dark (iconified) */
	    set_foreground(3, tb->dd);
	    XDrawString(tb->dd, tb->win, fore_gc, text_x, text_y + 1,
			tk->name, len);
	    set_foreground(4, tb->dd);
	} else {
	    set_foreground(5, tb->dd);
	}

	/* draw task's name here */
	XDrawString(tb->dd, tb->win, fore_gc, text_x, text_y, tk->name,
		    len);
    }

#ifndef HAVE_XPM
    if (!tk->icon)
	return;
#endif

    /* draw the task's icon */
    XSetClipMask(tb->dd, fore_gc, tk->mask);
    XSetClipOrigin(tb->dd, fore_gc, x + TEXTPAD,
		   y + (tb->rowheight - ICONHEIGHT) / 2);
    XCopyArea(tb->dd, tk->icon, tb->win, fore_gc, 0, 0, ICONWIDTH,
	      ICONHEIGHT, x + TEXTPAD,
	      y + (tb->rowheight - ICONHEIGHT) / 2);
    XSetClipMask(tb->dd, fore_gc, None);
}

void draw_dot(Window win, int x, int y, Display * dd)
{
    set_foreground(4, dd);
    XDrawPoint(dd, win, fore_gc, x, y);
    set_foreground(3, dd);
    XDrawPoint(dd, win, fore_gc, x + 1, y + 1);
}

void draw_grill(Window win, int x, int h, Display * dd)
{
    int y = 0;
    while (y < h - 4) {
	y += 3;
	draw_dot(win, x + 3, y, dd);
	draw_dot(win, x, y, dd);
    }
}
void gui_draw_taskbar(taskbar * tb)
{
    int xoffset, yoffset;
    int width, height;
    int nrows, ncols;
    int ntasks, ntot;		/* ntot = ntasks + empty spaces */
    int x, y;			/* individual task properties */
    task *tk;

    xoffset = TASKS_X;
    yoffset = 0;

    width = tb->width - xoffset;
    height = tb->height - yoffset;

    nrows = tb->height / tb->rowheight;

    ntasks = tb->num_tasks;
    ntot = ntasks + nrows - (ntasks % nrows);	/* no demo ;-) */

    /* Allow for full line */
    if ((ntot == ntasks + nrows) && ntasks > 0)
	ntot = ntasks;
    ncols = ntot / nrows;

    tk = tb->task_list;

    for (y = 0; y < nrows; y++) {
	for (x = 0; x < ncols; x++) {
	    if (ntasks > 0) {
		tk->pos_x = xoffset + x * width / ncols;
		tk->pos_y = yoffset + y * height / nrows;
		tk->width = width / ncols - 1;
		gui_draw_task(tb, tk);
		tk = tk->next;
		--ntasks;
	    } else {
		/* clear the rest */
		set_foreground(0, tb->dd);
		fill_rect(tb, xoffset + x * width / ncols,
			  yoffset + y * height / nrows, width / ncols - 1,
			  height / nrows);
		gui_draw_vline(tb, xoffset + x * width / ncols, y);
	    }
	}
    }
    draw_grill(tb->win, 2, tb->height, tb->dd);
}

task *find_task(taskbar * tb, Window win)
{
    task *list = tb->task_list;
    while (list) {
	if (list->win == win)
	    return list;
	list = list->next;
    }
    return 0;
}

void del_task(taskbar * tb, Window win)
{
    task *next, *prev = 0, *list = tb->task_list;

    while (list) {
	next = list->next;
	if (list->win == win) {
	    /* unlink and free this task */
	    tb->num_tasks--;
	    if (list->icon_copied) {
		XFreePixmap(tb->dd, list->icon);
		if (list->mask != None)
		    XFreePixmap(tb->dd, list->mask);
	    }
	    if (list->name)
		XFree(list->name);
	    free(list);
	    if (prev == 0)
		tb->task_list = next;
	    else
		prev->next = next;
	    return;
	}
	prev = list;
	list = next;
    }
}

void taskbar_read_clientlist(taskbar * tb)
{
    Window *win, focus_win;
    int num, i, rev, desk, new_desk = 0;
    task *list, *next;

    desk = find_desktop(root_win, tb->dd);
    if (desk != tb->my_desktop) {
	new_desk = 1;
	tb->my_desktop = desk;
    }

    XGetInputFocus(tb->dd, &focus_win, &rev);

    /* try unified window spec first */
    win = get_prop_data(root_win, atom__NET_CLIENT_LIST, XA_WINDOW,
			&num, tb->dd);
    if (!win) {
	/* failed, let's try gnome */
	win = get_prop_data(root_win, atom__WIN_CLIENT_LIST,
			    XA_CARDINAL, &num, tb->dd);
	if (!win)
	    return;
    }

    /* remove windows that arn't in the _WIN_CLIENT_LIST anymore */
    list = tb->task_list;
    while (list) {
	list->focused = (focus_win == list->win);
	next = list->next;

	if (!new_desk)
	    for (i = num - 1; i >= 0; i--)
		if (list->win == win[i])
		    goto dontdel;	/* Argh, another goto */
	del_task(tb, list->win);
      dontdel:

	list = next;
    }

    /* add any new windows */
    for (i = 0; i < num; i++) {
	if (!find_task(tb, win[i]))
	    add_task(tb, win[i], (win[i] == focus_win));
    }

    XFree(win);
}

void move_taskbar(taskbar * tb)
{
    int x, y;

    x = y = 0;

    if (tb->hidden)
	x = tb->width - TEXTPAD;

    if (!tb->at_top)
	y = scr_height - tb->height;

    XMoveWindow(tb->dd, tb->win, x, y);
}

void switch_desk(taskbar * tb, int rel)
{
    XClientMessageEvent xev;
    unsigned long *data;
    int want = tb->my_desktop + rel;

    if (want < 0)
	return;

    data = get_prop_data(root_win, atom__WIN_WORKSPACE_COUNT,
			 XA_CARDINAL, 0, tb->dd);
    if (data) {
	register unsigned long max_desks = *data;
	XFree(data);
	if (max_desks <= want)
	    return;
    }

    xev.type = ClientMessage;
    xev.window = root_win;
    xev.message_type = atom__WIN_WORKSPACE;
    xev.format = 32;
    xev.data.l[0] = want;
    XSendEvent(tb->dd, root_win, False, SubstructureNotifyMask,
	       (XEvent *) & xev);
}

void handle_press(taskbar * tb, int x, int y)
{
    task *tk;

    /*if (y > 3 && y < tb->winheight - 3)
       {
       if (x >= right_arrow_x && x < right_arrow_x + 9)
       {
       switch_desk (tb, +1);
       return;
       }

       if (x >= left_arrow_x && x < left_arrow_x + 9)
       {
       switch_desk (tb, -1);
       return;
       }
       } */

    /* clicked left grill -- let's implement something good someday. */
    /*if (x < 6)
       {
       if (tb->hidden)
       tb->hidden = 0;
       else
       tb->at_top = !tb->at_top;
       move_taskbar (tb);
       return;
       } */

    /* clicked right grill */
    /*if (x + TEXTPAD > tb->winwidth)
       {
       tb->hidden = !tb->hidden;
       move_taskbar (tb);
       return;
       } */

    tk = tb->task_list;
    while (tk) {
	if (x > tk->pos_x && x < tk->pos_x + tk->width &&
	    y > tk->pos_y && y < tk->pos_y + tb->rowheight) {
	    if (tk->iconified) {
		tk->iconified = 0;
		tk->focused = 1;
		XMapWindow(tb->dd, tk->win);
	    } else {
		if (tk->focused) {
		    tk->iconified = 1;
		    tk->focused = 0;
		    XIconifyWindow(tb->dd, tk->win, scr_screen);
		} else {
		    tk->focused = 1;
		    XRaiseWindow(tb->dd, tk->win);
		    XSetInputFocus(tb->dd, tk->win, RevertToNone,
				   CurrentTime);
		}
	    }
	    gui_sync(tb->dd);
	    gui_draw_task(tb, tk);
	} else {
	    if (tk->focused) {
		tk->focused = 0;
		gui_draw_task(tb, tk);
	    }
	}

	tk = tk->next;
    }
}

void handle_focusin(taskbar * tb, Window win)
{
    task *tk;

    tk = tb->task_list;
    while (tk) {
	if (tk->focused) {
	    if (tk->win != win) {
		tk->focused = 0;
		gui_draw_task(tb, tk);
	    }
	} else {
	    if (tk->win == win) {
		tk->focused = 1;
		gui_draw_task(tb, tk);
	    }
	}
	tk = tk->next;
    }
}

void handle_propertynotify(taskbar * tb, Window win, Atom at)
{
    task *tk;

    if (win == root_win) {
	if (at == atom__NET_CLIENT_LIST ||
	    at == atom__WIN_CLIENT_LIST || at == atom__WIN_WORKSPACE) {
	    taskbar_read_clientlist(tb);
	    gui_draw_taskbar(tb);
	}
	return;
    }

    tk = find_task(tb, win);
    if (!tk)
	return;

    if (at == XA_WM_NAME) {
	/* window's title changed */
	if (tk->name)
	    XFree(tk->name);
	tk->name = get_prop_data(tk->win, XA_WM_NAME, XA_STRING, 0,
				 tb->dd);
	gui_draw_task(tb, tk);
    } else if (at == atom_WM_STATE) {
	/* iconified state changed? */
	if (is_iconified(tk->win, tb->dd) != tk->iconified) {
	    tk->iconified = !tk->iconified;
	    gui_draw_task(tb, tk);
	}
    } else if (at == XA_WM_HINTS) {
	/* some windows set their WM_HINTS icon after mapping */
	if (tk->icon == generic_icon) {
	    get_task_hinticon(tk, tb->dd);
	    gui_draw_task(tb, tk);
	}
    }
}

void handle_error(Display * d, XErrorEvent * ev)
{
    fprintf(stderr, "Unhandled error : %i\n", ev->error_code);
}

void badargs()
{
    fprintf(stderr, "Unrecognised argument. Please look at man page.\n");
}

/* Limitation : pattern should be 3 chars long eg : -n= */
int checkarg(arg, pattern)
char *arg;
char *pattern;
{
    char matchme[6];
    int n;
    if (!strncmp(arg, pattern, 3)) {
	sprintf(matchme, "%s%%i", pattern);	/* eg: -n=%i */
	if (sscanf(arg, matchme, &n) > 0) {
	    /* printf("got %s = %i\n",pattern,n); */
	} else {
	    badargs();
	    return 0;
	}
    } else {
	return 0;
    }
    return n;
}

int manage_args(argc, argv, arg)
int argc;
char *argv[];
args *arg;
{
    int i;
    int n = 0;

    arg->winheight = 48;
    arg->winwidth = 500;
    arg->nrows = 2;
    arg->rowheight = 24;	/* keep this ? */
    arg->wintop = 0;

    for (i = 1; i < argc; i++) {
	if (n = checkarg(argv[i], "-n="))
	    arg->nrows = n;
	else if (n = checkarg(argv[i], "-h="))
	    arg->winheight = n;
	else if (n = checkarg(argv[i], "-w="))
	    arg->winwidth = n;
	else if (n = checkarg(argv[i], "-y="))
	    arg->wintop = n;
	else
	    badargs();
    }
    arg->rowheight = (arg->winheight / arg->nrows);
    return 0;
}

int main(int argc, char *argv[])
{
    args arguments;
    taskbar *tb = (taskbar *) malloc(sizeof(taskbar));
    XEvent ev;

    if (manage_args(argc, argv, &arguments) != 0) {
	printf("Error w/args\n");
	return 1;
    }

    tb->dd = XOpenDisplay(NULL);
    if (!tb->dd)
	return 0;

    /* FIXME bleeh, still global vars */
    scr_screen = DefaultScreen(tb->dd);
    scr_depth = DefaultDepth(tb->dd, scr_screen);
    scr_height = DisplayHeight(tb->dd, scr_screen);
    scr_width = DisplayWidth(tb->dd, scr_screen);
    root_win = RootWindow(tb->dd, scr_screen);

    /* helps us catch windows closing/opening */
    XSelectInput(tb->dd, root_win, PropertyChangeMask);

    XSetErrorHandler((XErrorHandler) handle_error);

    XInternAtoms(tb->dd, atom_names, ATOM_COUNT, False, atoms);

    gui_init(tb->dd);
    tb = gui_create_taskbar(arguments, tb->dd);
    gui_sync(tb->dd);

    while (1) {
	XNextEvent(tb->dd, &ev);
	switch (ev.type) {
	case ButtonPress:
	    if (ev.xbutton.button == 1)
		handle_press(tb, ev.xbutton.x, ev.xbutton.y);
	    break;
	case DestroyNotify:
	    del_task(tb, ev.xdestroywindow.window);
	    /* fall through */
	    break;		/* added with no understand. */
	case Expose:
	    gui_draw_taskbar(tb);
	    break;
	case PropertyNotify:
	    handle_propertynotify(tb, ev.xproperty.window,
				  ev.xproperty.atom);
	    break;
	case FocusIn:
	    handle_focusin(tb, ev.xfocus.window);
	    break;
	    /*default:
	       printf ("unknown evt type: %d\n", ev.type); */
	}
    }

/*	XCloseDisplay (dd);  FIXME : get reference to pointer to exit gfully,
 get here.*/

    return 0;
}
