/*
 * asmail is the AfterStep mailbox monitor
 * Copyright (c) 2002 Albert Dorofeev <albert@tigr.net>
 * For the updates see http://www.tigr.net/
 *
 * This software is distributed under GPL. For details see LICENSE file.
 */
/*
 * Ok, here is how it works:
 *
 * X11 is not always thread safe. Many libraries are even less so.
 * That's why we have to stick to a less then efficient implementation
 * of the X interface. Namely, single-threaded.
 *
 * All setup is done by a single thread. This thread creates the
 * windows and sets up the icons. Then it goes to sleep for the
 * refresh rate number of milliseconds.
 * When we wake up, we have to check:
 * 	- if the state of the mailboxes was updated
 * 	- if there is some animation to be done
 * 	- if there are any X events pending
 * When necessary, we redraw the whole thing then.
 */

#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/xpm.h>
#include <X11/Xatom.h>
#include <X11/extensions/shape.h>

#include <pthread.h>
#include <errno.h>
#include <time.h>

#include "globals.h"
#include "gui.h"
#include "x_color.h"

/*#define DEBUG 1*/

#define LEFTBUTTON	1	/* Left mousebutton id */
#define MIDDLEBUTTON	2	/* Middle mousebutton id */
#define RIGHTBUTTON	3	/* Right mousebutton id */

#include "pixmaps/frame.xpm"
#include "pixmaps/newmail.xpm"
#include "pixmaps/oldmail.xpm"
#include "pixmaps/nomail.xpm"
#include "pixmaps/newmail_s.xpm"
#include "pixmaps/oldmail_s.xpm"
#include "pixmaps/nomail_s.xpm"

/* Controls how close the color should be to the one we ask
 * for when we run out of color cells.
 * http://www.faqs.org/faqs/motif-faq/part5/section-34.html */
#define XPMCLOSENESS 40000

struct XpmIcon {
	Pixmap pixmap;
	Pixmap mask;
	XpmAttributes attributes;
	struct XpmIcon *next;
};
struct XpmIcon *Frame = NULL, 
	*NoMail = NULL, 
	*OldMail = NULL, 
	*NewMail = NULL, 
	*Current = NULL;

int saved_status = MAIL_NONE;
int need_animation = 0;

/* Here we work with lots of static variables so this
 * section will be protected by a mutex from multithreading. 
 * Actually, in the future we may make many windows here :) */
pthread_mutex_t x11_mutex = PTHREAD_MUTEX_INITIALIZER;

/* 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 */
Pixmap drawWindow;		/* Drawing window - prepare the final image */
XGCValues mainGCV;              /* graphics context values */
GC mainGC;                      /* Graphics context */
Atom wm_delete_window;
Atom wm_protocols;

XTextItem NumOfMsg;
char NumOfMsgText[MAX_INPUT_LENGTH+1];
Pixel NumOfMsgColor;
XFontStruct * font_struct;

/* Foreground / background colors for the label */
Pixel back_pix;
Pixel fore_pix;

/* The size of the window */
XPoint winsize = {64,64};

void XPMError(int Code, const char * info) {
	switch (Code) {
	case 0:
		break;
	case 1:
	case -4:
		printf("asmail: XPMError: %s: not enough free color cells\n",
				info);
		break;
	case -1:
	case -2:
		printf("asmail: XPMError: %s: could not load xpm\n",
				info);
		break;
	case -3:
		printf("asmail: XPMError: %s: not enough memory free\n",
				info);
		break;
	default:
		printf("asmail: XPMError: %s: unknown xpm-error\n",
				info);
		break;
	}
	if (Code != 0)
		exit(1);
}

void LoadXPM(struct pixfile * filelist, struct XpmIcon ** destination) {
	struct XpmIcon * icon;
	struct pixfile * file_ptr;

	file_ptr = filelist;
	while ( file_ptr ) {
		icon = (struct XpmIcon *) calloc(1, sizeof(struct XpmIcon));
		icon->attributes.valuemask |= XpmCloseness;
		icon->attributes.closeness = XPMCLOSENESS;
		XPMError(XpmReadFileToPixmap(mainDisplay, Root,
					file_ptr->name,
					&icon->pixmap,
					&icon->mask,
					&icon->attributes),
				file_ptr->name);
		icon->next = *destination;
		*destination = icon;
		file_ptr = file_ptr->next;
	}
}

struct XpmIcon *GetXPM(char **data) {
	struct XpmIcon *icon = 
		(struct XpmIcon *) calloc(1, sizeof(struct XpmIcon));

	icon->attributes.valuemask |= XpmCloseness;
	icon->attributes.closeness = XPMCLOSENESS;

	XPMError(XpmCreatePixmapFromData(mainDisplay, Root,
					 data,
					 &icon->pixmap,
					 &icon->mask,
					 &icon->attributes),
			"built-in");

	icon->next = (struct XpmIcon *) icon;

	return icon;
}

void execute_on_new() {
	if ( x11_set.beep )
		XBell(mainDisplay, 0);
	if ( strlen(x11_set.on_new_mail) )
		system(x11_set.on_new_mail);
}

void x_cleanup() {
	XFreeFont(mainDisplay, font_struct);
	XCloseDisplay(mainDisplay);
	exit(0);
}

void add_status(char * line, int s) {
#ifdef DEBUG
	printf("asmail: add_status: %d\n", s);
#endif
	if ( s & STAT_RUN )
		sprintf(&line[strlen(line)], "R");
	else
		sprintf(&line[strlen(line)], " ");
	if ( s & STAT_FAIL )
		sprintf(&line[strlen(line)], "F");
	else if ( s & STAT_CONN )
		sprintf(&line[strlen(line)], "C");
	else if ( s & STAT_LOGIN )
		sprintf(&line[strlen(line)], "L");
	else if ( s & STAT_TIMEOUT )
		sprintf(&line[strlen(line)], "T");
	else
		sprintf(&line[strlen(line)], " ");
}

void setup_window(Window win) {
	int x,y;

	if ( ! x11_set.shape )
		return;
#ifdef DEBUG
	printf("asmail: setup_window: Setting up the window borders...\n");
#endif
	/* Offset for the image shape (centered) */
	x = (winsize.x - Current->attributes.width)/2;
	y = (winsize.y - Current->attributes.height)/2;
	if ( x11_set.use_frame ) {
		XShapeCombineMask(mainDisplay, win, ShapeBounding, 0, 0,
				Frame->mask, ShapeSet);
		XShapeCombineMask(mainDisplay, win, ShapeBounding, x, y,
				Current->mask, ShapeUnion);
	} else {
		XShapeCombineMask(mainDisplay, win, ShapeBounding, x, y,
				Current->mask, ShapeSet);
	}
}

void draw_window(Window win) {
	int x,y;
	struct mbox_struct * mb;
	int o = 0;
	int n = 0;
	int s = STAT_IDLE;
	XSizeHints SizeHints;
	int direction_return, ascent_return, descent_return;
	XCharStruct overall_return;
	int mail_status;

#ifdef DEBUG
	printf("asmail: draw_window: Redrawing the window...\n");
#endif
/*	x = (Frame->attributes.width - Current->attributes.width)/2;
	y = (Frame->attributes.height - Current->attributes.height)/2;*/
	/* Offset for clip origin (centered) */
	x = (winsize.x - Current->attributes.width)/2;
	y = (winsize.y - Current->attributes.height)/2;
	if ( x11_set.use_frame ) {
		XCopyArea(mainDisplay, Frame->pixmap, drawWindow, mainGC, 0, 0,
				winsize.x, winsize.y,
				0, 0);
	}
	if ( x11_set.shape ) {
		XSetClipOrigin(mainDisplay, mainGC, x, y);
		XSetClipMask(mainDisplay, mainGC, Current->mask);
	}
	XCopyArea(mainDisplay, Current->pixmap, drawWindow, mainGC, 0, 0,
			Current->attributes.width, Current->attributes.height,
			x, y);
	if ( x11_set.shape )
		XSetClipMask(mainDisplay, mainGC, None);
	/*
	 * Display the summary of the mailboxes
	 */
	mb = mbox;
	while (mb) {
		if ( mb->ctotal )
			o += mb->ctotal;
		if ( mb->cnew )
			n += mb->cnew;
		s |= mb->status;
		mb = mb->next;
	}
#ifdef DEBUG
	printf("asmail: draw_window: message count: %d new, %d old\n", n, o);
#endif
	if ( x11_set.total ) {
		NumOfMsg.chars[0] = '\0';
		if ( x11_set.status ) {
			add_status(NumOfMsg.chars, s);
			sprintf(&NumOfMsg.chars[strlen(NumOfMsg.chars)], " ");
		}
		if ( x11_set.new ) {
			if ( 0 != n )
				sprintf(&NumOfMsg.chars[strlen(NumOfMsg.chars)],
						"%d", n);
			else
				sprintf(&NumOfMsg.chars[strlen(NumOfMsg.chars)],
						"-");
		}
		if ( x11_set.old && x11_set.new ) {
			sprintf(&NumOfMsg.chars[strlen(NumOfMsg.chars)], 
					x11_set.delimiter);
		}
		if ( x11_set.old ) {
			if ( 0 != o )
				sprintf(&NumOfMsg.chars[strlen(NumOfMsg.chars)],
						"%d", o);
			else
				sprintf(&NumOfMsg.chars[strlen(NumOfMsg.chars)],
						"-");
		}
		NumOfMsg.nchars = strlen(NumOfMsg.chars);
		XDrawText(mainDisplay, drawWindow, mainGC, 
				x + x11_set.x, y + x11_set.y, 
				&NumOfMsg, 1);
		XTextExtents(font_struct, NumOfMsg.chars, 
				strlen(NumOfMsg.chars),
				&direction_return, &ascent_return,
				&descent_return, &overall_return);
		y += ascent_return + descent_return + 2;
	}
	/*
	 * Display information for each mailbox
	 */
	if ( x11_set.each ) {
		mb = mbox;
		while (mb) {
			NumOfMsg.chars[0] = '\0';
			if ( x11_set.status ) {
				add_status(NumOfMsg.chars, mb->status);
				sprintf(&NumOfMsg.chars[strlen(NumOfMsg.chars)], " ");
			}
			if ( x11_set.new ) {
				if ( 0 != mb->cnew )
					sprintf(&NumOfMsg.chars[strlen(NumOfMsg.chars)], "%d", mb->cnew);
				else
					sprintf(&NumOfMsg.chars[strlen(NumOfMsg.chars)], "-");
			}
			if ( x11_set.old && x11_set.new ) {
				sprintf(&NumOfMsg.chars[strlen(NumOfMsg.chars)],
						x11_set.delimiter);
			}
			if ( x11_set.old ) {
				if ( 0 != mb->ctotal )
					sprintf(&NumOfMsg.chars[strlen(NumOfMsg.chars)], "%d", mb->ctotal);
				else
					sprintf(&NumOfMsg.chars[strlen(NumOfMsg.chars)], "-");
			}
			NumOfMsg.nchars = strlen(NumOfMsg.chars);
			XDrawText(mainDisplay, drawWindow, mainGC, 
					x + x11_set.x, y + x11_set.y, 
					&NumOfMsg, 1);
			XTextExtents(font_struct, NumOfMsg.chars, 
					strlen(NumOfMsg.chars),
					&direction_return, &ascent_return,
					&descent_return, &overall_return);
			y += ascent_return + descent_return + 2;

			mb = mb->next;
		}
	}
	/*
	 * One final step - copy contents from the temp drawing
	 * window into the actual window.
	 */
	XCopyArea(mainDisplay, drawWindow, win, mainGC, 0, 0,
			winsize.x, winsize.y,
			0, 0);
}

void setup_pixmap() {
#ifdef DEBUG
	printf("asmail: setup_pixmap: Choosing pixmap...\n");
#endif
	switch (x11_set.status) {
		case MAIL_NONE: 
			Current = NoMail; 
			break;
		case MAIL_OLD: 
			Current = OldMail; 
			break;
		case MAIL_NEW: 
			Current = NewMail; 
			break;
	}
}

void redraw() {
#ifdef DEBUG
	printf("asmail: redraw: Redrawing the application windows...\n");
#endif
	setup_window(mainWindow);
	draw_window(mainWindow);
	setup_window(iconWindow);
	draw_window(iconWindow);
	XFlush(mainDisplay);
}

/*
 * Verify the status of mailboxes. If the status changes,
 * return 1, otherwise return 0. If the status changes,
 * call to set up the correct icon.
 */
int check_mbox() {
	struct mbox_struct * mb;

	mb = mbox;
	x11_set.status = MAIL_NONE;
	while ( mb ) {
		if ( mb->mail == MAIL_NEW )
			x11_set.status = MAIL_NEW;
		else if ( (mb->mail == MAIL_OLD) && (x11_set.status != MAIL_NEW) )
			x11_set.status = MAIL_OLD;
		if ( mb->flags & FLAG_ARRIVED ) {
			pthread_mutex_lock(&mb->mutex);
			mb->flags &= ! FLAG_ARRIVED;
			pthread_mutex_unlock(&mb->mutex);
			execute_on_new();
		}
		mb = mb->next;
	}
	if ( saved_status != x11_set.status ) {
#ifdef DEBUG
	printf("asmail: check_mbox: Determined mail status as %s (was %s)\n",
			x11_set.status == MAIL_NEW ? "NEW" :
			x11_set.status == MAIL_OLD ? "OLD" : "NONE",
			saved_status == MAIL_NEW ? "NEW" :
			saved_status == MAIL_OLD ? "OLD" : "NONE");
#endif
		setup_pixmap();
		saved_status = x11_set.status;
		if ( Current && Current->next && Current->next != Current )
			need_animation = 1;
		else
			need_animation = 0;
		return 1;
	}
	return 0;
}

void ButtonHandler(XEvent * E) {
	if (E->xbutton.button == LEFTBUTTON) {
		if (strlen(x11_set.on_left))
			system(x11_set.on_left);
	} else if (E->xbutton.button == MIDDLEBUTTON) {
		if (strlen(x11_set.on_middle))
			system(x11_set.on_middle);
	} else if (E->xbutton.button == RIGHTBUTTON) {
		if (strlen(x11_set.on_right))
			system(x11_set.on_right);
	}
}

void x11_event()
{
	XEvent Event;
	while ( XPending(mainDisplay) ) {
		XNextEvent(mainDisplay, &Event);
		switch (Event.type) {
		case Expose:
#ifdef DEBUG
			printf("asmail: x11_event: Expose event caught: (%d %d) (%d x %d)\n",
			       ((XExposeEvent *) & Event)->x,
			       ((XExposeEvent *) & Event)->y,
			       ((XExposeEvent *) & Event)->width,
			       ((XExposeEvent *) & Event)->height);
#endif
			if (Event.xexpose.count == 0) {
				/*signal_xkick();*/
				draw_window(((XExposeEvent *) & Event)->window);
			}
			break;
		case ButtonPress:
			ButtonHandler(&Event);
			break;
		case ClientMessage:
			if ((Event.xclient.message_type == wm_protocols)
			    && (Event.xclient.data.l[0] == wm_delete_window)) {
#ifdef DEBUG
				printf("asmail: caught wm_delete_window, closing\n");
#endif
				x_cleanup();
			}
			break;
		}
	}
}

void main_loop() {
	XEvent Event;
	int need_redraw;

	check_mbox();
	redraw();
	x11_event();

	while (1) {
		/* sleep_update will return 1 if it was signalled
		 * and not simply timed out */
		need_redraw = sleep_update( x11_set.refresh );
#ifdef DEBUG
		printf("asmail: main_loop: awake because of %s\n",
				need_redraw ? "SIGNAL" : "TIMEOUT");
#endif

		if ( need_animation ) {
			if ( Current )
				if ( Current->next )
					Current = Current->next;
				else
					setup_pixmap();
			need_redraw = 1;
		}

		if ( check_mbox() )
			need_redraw = 1;

		if ( need_redraw )
			redraw();

		/* Check if there are events pending for us.
		 * Usually, if we redraw, there will be Expose
		 * events - so check for events last. */
		x11_event();
	}
}

void startx(void * ptr) {
	int kicks;
	int screen;
	int color_depth;
	XSizeHints SizeHints;
	int result;
	int x_negative = 0;
	int y_negative = 0;
	int x_size_forced = 0;
	int y_size_forced = 0;
	Status status;
	XTextProperty title;
	XClassHint classHint;
	XWMHints WmHints;
	XEvent Event;
	char * char_p;

	if ( pthread_mutex_trylock(&x11_mutex) == EBUSY ) {
		printf("asmail: startx: non-reentrant function!\n");
		exit(0);
	}

	/* Use the $DISPLAY display */
	mainDisplay = XOpenDisplay(NULL);
	if (!mainDisplay) {
		printf("asmail: startx: can't open display %s\n",
				XDisplayName(NULL));
		exit(1);
	}
	screen = DefaultScreen(mainDisplay);
	Root = RootWindow(mainDisplay, screen);
	back_pix = GetColor("#385971", mainDisplay, Root);
	fore_pix = GetColor(x11_set.color, mainDisplay, Root);
	color_depth = DefaultDepth(mainDisplay, screen);
#ifdef DEBUG
	printf("asmail: detected color depth %d bpp\n", color_depth);
#endif
	/* Load the label font */
	if ( x11_set.each || x11_set.total ) {
		font_struct = XLoadQueryFont( mainDisplay, x11_set.font );
		if ( ! font_struct ) {
			printf("asmail: failed to load font %s\n",
					x11_set.font);
			printf("asmail: the indicators of message numbers");
			printf("will not be drawn\n");
			x11_set.each = 0;
			x11_set.total = 0;
		}
#ifdef DEBUG
		else {
			printf("asmail: loaded font %s\n", x11_set.font);
		}
#endif
	}
	NumOfMsg.chars = NumOfMsgText;
	NumOfMsg.font = XLoadFont(mainDisplay, x11_set.font);
	NumOfMsgColor = fore_pix;

	/* Here we have 2 possibilities. Either there are no pics
	 * given in the config file -> use the built-in and no
	 * animation. Or we got a list of pics and we will have
	 * to animate through that list. */
	if ( x11_set.nomail ) {
		LoadXPM(x11_set.nomail, &NoMail);
	} else {
		NoMail = GetXPM((x11_set.shape) ? nomail_s : nomail);
		NoMail->next = NoMail;
	}
	if ( x11_set.oldmail ) {
		LoadXPM(x11_set.oldmail, &OldMail);
	} else {
		OldMail = GetXPM((x11_set.shape) ? oldmail_s : oldmail);
		OldMail->next = OldMail;
	}
	if ( x11_set.newmail ) {
		LoadXPM(x11_set.newmail, &NewMail);
	} else {
		NewMail = GetXPM((x11_set.shape) ? newmail_s : newmail);
		NewMail->next = NewMail;
	}
	if ( x11_set.frame ) {
		LoadXPM(x11_set.frame, &Frame);
	} else {
		if ( x11_set.use_frame ) {
			Frame = GetXPM(frame);
			Frame->next = Frame;
		}
	}
	Current = NoMail;

#ifdef DEBUG
	printf("asmail: frame %s, nomail %s, old-mail %s, new-mail %s\n",
			Frame ? "yes":"no", NoMail ? "yes":"no",
			OldMail ? "yes":"no", NewMail ? "yes":"no");
	if ( Frame )
		printf("asmail: frame %d depth, %d x %d\n",
				Frame->attributes.depth,
				Frame->attributes.width,
				Frame->attributes.height);
	if ( Current )
		printf("asmail: first picture %d depth, %d x %d\n",
				Current->attributes.depth,
				Current->attributes.width,
				Current->attributes.height);
#endif

	SizeHints.x = 0;
	SizeHints.y = 0;
	if ( x11_set.use_frame ) {
		winsize.x = Frame->attributes.width;
		winsize.y = Frame->attributes.height;
	} else {
		/* No frame is used, we have to figure
		 * out the size of the window ourselves */
		winsize.x = Current->attributes.width;
		winsize.y = Current->attributes.height;
	}
	SizeHints.flags = USSize | USPosition;

	/* Parsing the geometry */
	if ( strlen(x11_set.geometry) ) {
		result = XParseGeometry(x11_set.geometry,
				&SizeHints.x,
				&SizeHints.y,
				&SizeHints.width,
				&SizeHints.height);
		if (result & WidthValue) {
			x_size_forced = 1;
			winsize.x = SizeHints.width;
		}
		if (result & HeightValue) {
			y_size_forced = 1;
			winsize.y = SizeHints.height;
		}
		if (result & XNegative)
			x_negative = 1;
		if (result & YNegative)
			y_negative = 1;
	}

	/* Make it non-resizeable */
	SizeHints.min_width = SizeHints.max_width = SizeHints.width = winsize.x;
	SizeHints.min_height = SizeHints.max_height = SizeHints.height = winsize.y;
	SizeHints.flags |= PMinSize | PMaxSize;
	XWMGeometry(mainDisplay, screen, x11_set.geometry, NULL, 0,
			&SizeHints, &SizeHints.x, &SizeHints.y,
			&SizeHints.width, &SizeHints.height, 
			&SizeHints.win_gravity);
	/* Correct the gravity for correct offsets if the X/Y are negative */
	SizeHints.win_gravity = NorthWestGravity;
	if (x_negative) {
		SizeHints.win_gravity = NorthEastGravity;
	}       
	if (y_negative) {
		if (x_negative)
			SizeHints.win_gravity = SouthEastGravity;
		else    
			SizeHints.win_gravity = SouthWestGravity;
	}
	SizeHints.flags |= PWinGravity;
#ifdef DEBUG
	printf("asmail: Size %d x %d, Position %d %d, gravity %d\n",
			SizeHints.width, SizeHints.height,
			SizeHints.x, SizeHints.y, SizeHints.win_gravity);
	printf("        (gravity: NW %d, NE %d, SE %d, SW %d)\n",
			NorthWestGravity, NorthEastGravity,
			SouthEastGravity, SouthWestGravity);
#endif
	drawWindow = XCreatePixmap(
				mainDisplay,		/* display */
				Root,			/* parent */
				winsize.x,	/* width */
				winsize.y,	/* height */
				color_depth		/* color depth */
	    );
	mainWindow = XCreateSimpleWindow(
				mainDisplay,		/* display */
				Root,			/* parent */
				SizeHints.x,		/* x */
				SizeHints.y,		/* y */
				winsize.x,	/* width */
				winsize.y,	/* height */
				0,			/* border_width */
				fore_pix,		/* border */
				back_pix		/* background */
		);
	iconWindow = XCreateSimpleWindow(
				mainDisplay,		/* display */
				Root,			/* parent */
				SizeHints.x,		/* x */
				SizeHints.y,		/* y */
				winsize.x,	/* width */
				winsize.y,	/* 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);

	char_p = x11_set.title;
	status = XStringListToTextProperty(&char_p, 1, &title);
	XSetWMName(mainDisplay, mainWindow, &title);
	XSetWMName(mainDisplay, iconWindow, &title);

	classHint.res_name = "asmail";
	classHint.res_class = "ASMAIL";
	XSetClassHint(mainDisplay, mainWindow, &classHint);
	XStoreName(mainDisplay, mainWindow, x11_set.title);
	XSetIconName(mainDisplay, mainWindow, x11_set.title);

	/* We need some events */
	status = XSelectInput(
			mainDisplay,
			mainWindow,
			ExposureMask | ButtonPressMask
			);
	status = XSelectInput(
			mainDisplay,
			iconWindow,
			ExposureMask | ButtonPressMask
			);

	/* Remember the command line */
	status = XSetCommand(mainDisplay, mainWindow, 
			x11_set.argv, x11_set.argc);

	/* Set up the event for quitting the window */
	wm_delete_window = XInternAtom(mainDisplay,
			"WM_DELETE_WINDOW",
			False
			);
	wm_protocols = XInternAtom(mainDisplay,
			"WM_PROTOCOLS",
			False
			);
	status = XSetWMProtocols(mainDisplay,
			mainWindow,
			&wm_delete_window,
			1
			);
	status = XSetWMProtocols(mainDisplay,
			iconWindow,
			&wm_delete_window,
			1
			);
	WmHints.flags = StateHint | IconWindowHint;
	WmHints.initial_state =
		x11_set.withdrawn ? WithdrawnState :
		x11_set.iconic ? IconicState : NormalState;
	WmHints.icon_window = iconWindow;
	if (x11_set.withdrawn) {
		WmHints.window_group = mainWindow;
		WmHints.flags |= WindowGroupHint;
	}
	if (x11_set.iconic || x11_set.withdrawn) {
		WmHints.icon_x = SizeHints.x;
		WmHints.icon_y = SizeHints.y;
		WmHints.flags |= IconPositionHint;
	}
	XSetWMHints(mainDisplay, mainWindow, &WmHints);

	mainGCV.foreground = fore_pix;
	mainGCV.background = back_pix;
	mainGCV.graphics_exposures = False;
	mainGC = XCreateGC(mainDisplay, Root, 
			GCForeground | GCBackground,
			&mainGCV);

	status = XMapWindow(mainDisplay, mainWindow);

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

	main_loop();

	XFreeFont(mainDisplay, font_struct);
	XCloseDisplay(mainDisplay);
	pthread_exit(NULL);
}

