/*
 *     XaoS, a fast portable realtime fractal zoomer
 *                  Copyright (C) 1996 by
 *
 *      Jan Hubicka          (hubicka@paru.cas.cz)
 *      Thomas Marsh         (tmarsh@austin.ibm.com)
 *
 * 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.
 */
#include <config.h>
#undef _EFENCE_
#ifdef _plan9_
#include <u.h>
#include <libc.h>
#include <ctype.h>
#else
#include <limits.h>
#include <aconfig.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#ifdef __EMX__
#include <float.h>
#endif
#ifdef __EMX__
#include <sys/types.h>
#endif
#ifndef _MAC
#include <sys/stat.h>
#endif
#include <time.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <signal.h>
#endif
#include <fconfig.h>
#ifndef _plan9_
#include <assert.h>
#endif
#include <filter.h>
#include <ui_helper.h>
#include <ui.h>
#include <param.h>
#include <version.h>
#include <timers.h>
#include <plane.h>
#include <xthread.h>
#include <xerror.h>
#include <xmenu.h>
#include <grlib.h>
#include <archaccel.h>
#include "uiint.h"
#ifdef HAVE_GETTEXT
#include <libintl.h>
#include <locale.h>
#else
#define gettext(STRING) STRING
#endif

#ifdef SFFE_USING
#include "sffe.h"
#endif

#ifdef DESTICKY
int euid, egid;
#endif
#ifdef DEBUG
#ifdef __linux__
#include <malloc.h>
#define MEMCHECK
#endif
#endif
#define textheight1 (driver->textheight)
#define textwidth1 (driver->textwidth)
#define ui_flush() (driver->flush?driver->flush(),1:0)
#ifdef MEMCHECK
#define STATUSLINES 13
#else
#define STATUSLINES 11
#endif
static void ui_mouse(int mousex, int mousey, int mousebuttons,
		     int iterchange);
#ifndef exit_xaos
#define exit_xaos(i) exit(i)
#endif

xio_pathdata configfile;
static void ui_unregistermenus(void);
static void ui_mkimages(int, int);
static void main_loop(void) NORETURN;

int prog_argc;
int err;
char **prog_argv;
/*UI state */
uih_context *uih;
CONST struct ui_driver *driver;
char statustext[256];
int ui_nogui;
static struct image *image;
static int statusstart;
static struct uih_window *statuswindow = NULL;
static int ministatusstart;
static struct uih_window *ministatuswindow = NULL;
static int mouse;
/* Used by ui_mouse */
static int dirty = 0;
static int lastiter;
static int maxiter;
static int lastbuttons, lastx, lasty;
static int callresize = 0;
static tl_timer *maintimer;
static tl_timer *arrowtimer;
static tl_timer *loopt;
static int todriver = 0;

/* Command line variables */
static char *defpipe;
static char *defdriver = NULL;
static int deflist;
static int printconfig;
static int printspeed;
static int delaytime = 0;
static int defthreads = 0;
static int maxframerate = 80;
static float defscreenwidth = 0.0, defscreenheight = 0.0, defpixelwidth =
    0.0, defpixelheight = 0.0;

#ifdef SFFE_USING
char *sffeform = NULL;
char *sffeinit = NULL;
#endif

CONST struct params global_params[] = {
    {"-delay", P_NUMBER, &delaytime,
     "Delay screen updates (milliseconds)"},
    {"-driver", P_STRING, &defdriver, "Select driver"},
    {"-list", P_SWITCH, &deflist, "List available drivers. Then exit"},
    {"-config", P_SWITCH, &printconfig, "Print configuration. Then exit"},
    {"-speedtest", P_SWITCH, &printspeed,
     "Test speed of calculation loop. Then exit"},
#ifndef nthreads
    {"-threads", P_NUMBER, &defthreads,
     "Set number of threads (CPUs) to use"},
#else
    {"-threads", P_NUMBER, &defthreads,
     "Multiple CPUs unsupported - please recompile XaoS with threads enabled"},
#endif
#ifdef COMPILE_PIPE
    {"-pipe", P_STRING, &defpipe,
     "Accept commands from pipe (use \"-\" for stdin)"},
#else
    {"-pipe", P_STRING, &defpipe,
     "Pipe input unavailable (recompile XaoS)"},
#endif
    {"-maxframerate", P_NUMBER, &maxframerate,
     "Maximal framerate (0 for unlimited - default)"},
    {"", P_HELP, NULL,
     "Screen size options: \n\n  Knowledge of exact screen size makes random dot stereogram look better. \n  Also is used for choosing correct view area"},
    {"-screenwidth", P_FLOAT, &defscreenwidth,
     "exact size of screen in centimeters"},
    {"-screenheight", P_FLOAT, &defscreenheight,
     "exact size of screen in centimeters"},
    {"", P_HELP, NULL,
     "  Use this option in case you use some kind of virtual screen\n  or something similiar that confuses previous options"},
    {"-pixelwidth", P_FLOAT, &defpixelwidth,
     "exact size of one pixel in centimeters"},
    {"-pixelheight", P_FLOAT, &defpixelheight,
     "exact size of one pixel in centimeters"},
#ifdef SFFE_USING
    {"-formula", P_STRING, &sffeform,
     "user formula"},
    {"-forminit", P_STRING, &sffeinit,
     "z0 for user formula"},
#endif
    {NULL, 0, NULL, NULL}
};

static int resizeregistered = 0;
static void ui_updatemenus(uih_context * c, CONST char *name)
{
    CONST struct menuitem *item;
    if (ui_nogui) {
	if (name == NULL) {
	    printf("Root \"%s\"", uih->menuroot);
	}
	item = menu_findcommand(name);
	if (item == NULL) {
	    /*x_fatalerror ("Internall error:unknown command %s", name); */
	    return;
	}
	if (item->flags & MENUFLAG_CHECKBOX) {
	    if (menu_enabled(item, c))
		printf("checkbox \"%s\" on\n", name);
	    else
		printf("checkbox \"%s\" off\n", name);
	}
	if (item->flags & MENUFLAG_RADIO) {
	    if (menu_enabled(item, c))
		printf("radio \"%s\"\n", name);
	}
    }
    if (driver != NULL && driver->gui_driver) {
	if (name == NULL) {
	    if (driver->gui_driver->setrootmenu)
		driver->gui_driver->setrootmenu(c, uih->menuroot);
	    return;
	}
	item = menu_findcommand(name);
	if (item == NULL) {
	    /*fprintf (stderr, "Internall error:unknown command %s\n", name); */
	    return;
	}
	if (item->flags & (MENUFLAG_CHECKBOX | MENUFLAG_RADIO)) {
	    if (driver->gui_driver->enabledisable)
		driver->gui_driver->enabledisable(uih, name);
	}
    }
}

static void mousetype(int m)
{
#ifdef _plan9_
#define filevisible 0
#endif
    if (ui_nmenus || helpvisible || filevisible || dialogvisible
	|| yesnodialogvisible)
	m = NORMALMOUSE;
    if (mouse != m) {
	mouse = m;
	if (driver->mousetype != NULL)
	    driver->mousetype(m);
    }
}

static void ui_display(void)
{
    if (nthreads == 1)
	uih_drawwindows(uih);
    driver->display();
    uih_cycling_continue(uih);
    if (!(driver->flags & NOFLUSHDISPLAY))
	ui_flush();
}

float ui_get_windowwidth(int width)
{
    if (defscreenwidth > 0.0 && driver->flags & RESOLUTION)
	return (defscreenwidth * width / driver->maxwidth);
    if (defscreenwidth > 0.0)
	return (defscreenwidth);
    if (defpixelwidth > 0.0)
	return (defpixelwidth * width);
    return (0);
}

static float get_windowwidth(int width)
{
    float w = ui_get_windowwidth(width);
    if (w)
	return w;
    if (driver->flags & PIXELSIZE)
	return (driver->width * width);
    if (driver->flags & SCREENSIZE)
	return (driver->width);
    if (driver->flags & RESOLUTION)
	return (29.0 / driver->maxwidth * width);
    return (29.0);
}

float ui_get_windowheight(int height)
{
    if (defscreenheight > 0.0 && driver->flags & RESOLUTION)
	return (defscreenheight * height / driver->maxheight);
    if (defscreenheight > 0.0)
	return (defscreenheight);
    if (defpixelheight > 0.0)
	return (defpixelheight * height);
    return 0;
}

static float get_windowheight(int height)
{
    float h = ui_get_windowheight(height);
    if (h)
	return h;
    if (driver->flags & PIXELSIZE)
	return (driver->height * height);
    if (driver->flags & SCREENSIZE)
	return (driver->height);
    if (driver->flags & RESOLUTION)
	return (21.0 / driver->maxheight * height);
    return (21.5);
}

extern int dynsize;
static void ui_outofmem(void)
{
    x_error(gettext("XaoS is out of memory."));
}

#define CHECKPROCESSEVENTS(b,k) assert(!((k)&~15)&&!((b)&~(BUTTON1|BUTTON2|BUTTON3)))
static int
ui_passfunc(struct uih_context *c, int display, CONST char *text,
	    float percent)
{
    char str[80];
    int x = 0, y = 0, b = 0, k = 0;
    driver->processevents(0, &x, &y, &b, &k);
    ui_mouse(x, y, b, k);
    CHECKPROCESSEVENTS(b, k);
    if (!uih->play) {
	if (uih->display)
	    ui_display(), display = 1;
	if (!c->interruptiblemode && !uih->play) {
	    if (display) {
		if (percent)
		    sprintf(str, "%s %3.2f%%        ", text,
			    (double) percent);
		else
		    sprintf(str, "%s          ", text);
		driver->print(0, uih->image->height - textheight1, str);
		ui_flush();
	    }
	} else {
	    if (!(driver->flags & NOFLUSHDISPLAY))
		ui_flush();
	}
    }
    return (0);
}

static void ui_updatestatus(void)
{
    double times =
	(uih->fcontext->currentformula->v.rr) / (uih->fcontext->s.rr);
    double timesnop = log(times) / log(10.0);
    double speed;
    uih_drawwindows(uih);
    driver->display();
    uih_cycling_continue(uih);
    speed = uih_displayed(uih);
    sprintf(statustext,
	    gettext
	    ("%s %.2f times (%.1fE) %2.2f frames/sec %c %i %i %i %i            "),
	    times < 1 ? gettext("unzoomed") : gettext("zoomed"),
	    times < 1 ? 1.0 / times : times, timesnop, speed,
	    uih->autopilot ? 'A' : ' ', uih->fcontext->coloringmode + 1,
	    uih->fcontext->incoloringmode + 1, uih->fcontext->plane + 1,
	    uih->fcontext->maxiter);

    if (!(driver->flags & NOFLUSHDISPLAY))
	ui_flush();
    STAT(printf(gettext("framerate:%f\n"), speed));
    driver->print(0, 0, "");
}

void ui_updatestarts(void)
{
    int y = 0;
    y += ui_menuwidth();
    ministatusstart = y;
    if (ministatuswindow != NULL)
	y += xtextheight(uih->font);
    statusstart = y;
    if (statuswindow != NULL)
	y += xtextheight(uih->font) * STATUSLINES;
    uih->messg.messagestart = y;
}

void ui_menuactivate(CONST menuitem * item, dialogparam * d)
{
    if (item == NULL)
	return;
    ui_closemenus();
    if (item->type == MENU_SUBMENU) {
	ui_menu(item->shortname);
	return;
    } else {
	if (menu_havedialog(item, uih) && d == NULL) {
	    ui_builddialog(item);
	    return;
	}
	if (uih->incalculation && !(item->flags & MENUFLAG_INCALC)) {
	    menu_addqueue(item, d);
	    if (item->flags & MENUFLAG_INTERRUPT)
		uih_interrupt(uih);
	    return;
	}
	if (item->flags & MENUFLAG_CHECKBOX) {
	    char s[256];
	    ui_updatestatus();
	    if (!menu_enabled(item, uih))
		sprintf(s, gettext("Enabling: %s. "), item->name);
	    else
		sprintf(s, gettext("Disabling: %s. "), item->name);
	    uih_message(uih, s);
	    ui_flush();
	} else
	    uih_message(uih, item->name);
	uih_saveundo(uih);
	menu_activate(item, uih, d);
	if (d != NULL)
	    menu_destroydialog(item, d, uih);
    }
}

xio_path ui_getfile(CONST char *basename, CONST char *extension)
{
    return (xio_getfilename(basename, extension));
}

static void
ui_statuspos(uih_context * uih, int *x, int *y, int *w, int *h, void *data)
{
    *x = 0;
    *y = statusstart;
    *w = uih->image->width;
    *h = xtextheight(uih->font) * STATUSLINES;
}

static void ui_drawstatus(uih_context * uih, void *data)
{
    char str[6000];
    int h = xtextheight(uih->font);
    sprintf(str, gettext("Fractal name:%s"),
	    uih->fcontext->currentformula->name[!uih->fcontext->
						mandelbrot]);
    xprint(uih->image, uih->font, 0, statusstart, str, uih->encoding,
	   FGCOLOR(uih), BGCOLOR(uih), 0);
    sprintf(str, gettext("Fractal type:%s"),
	    uih->
	    fcontext->mandelbrot ? gettext("Mandelbrot") :
	    gettext("Julia"));
#ifdef SFFE_USING
    if (uih->fcontext->currentformula->flags & SFFE_FRACTAL) {
	sprintf(str, gettext("Formula:%s"), uih->parser->expression);
    };
#endif
    xprint(uih->image, uih->font, 0, statusstart + h, str, uih->encoding,
	   FGCOLOR(uih), BGCOLOR(uih), 0);
    sprintf(str, gettext("View:[%1.12f,%1.12f]"),
	    (double) uih->fcontext->s.cr, (double) uih->fcontext->s.ci);
    xprint(uih->image, uih->font, 0, statusstart + 2 * h, str,
	   uih->encoding, FGCOLOR(uih), BGCOLOR(uih), 0);
    sprintf(str, gettext("size:[%1.12f,%1.12f]"),
	    (double) uih->fcontext->s.rr, (double) uih->fcontext->s.ri);
    xprint(uih->image, uih->font, 0, statusstart + 3 * h, str,
	   uih->encoding, FGCOLOR(uih), BGCOLOR(uih), 0);
    sprintf(str, gettext("Rotation:%4.2f   Screen size:%i:%i"),
	    (double) uih->fcontext->angle, uih->image->width,
	    uih->image->height);
    xprint(uih->image, uih->font, 0, statusstart + 4 * h, str,
	   uih->encoding, FGCOLOR(uih), BGCOLOR(uih), 0);
    sprintf(str, gettext("Iterations:%-4i Palette size:%i"),
	    uih->fcontext->maxiter, uih->image->palette->size);
    xprint(uih->image, uih->font, 0, statusstart + 5 * h, str,
	   uih->encoding, FGCOLOR(uih), BGCOLOR(uih), 0);
    sprintf(str, "Bailout:%4.2f", (double) uih->fcontext->bailout);
    xprint(uih->image, uih->font, 0, statusstart + 6 * h, str,
	   uih->encoding, FGCOLOR(uih), BGCOLOR(uih), 0);
    sprintf(str, gettext("Autopilot:%-4s  Plane:%s"),
	    uih->autopilot ? gettext("On") : gettext("Off"),
	    planename[uih->fcontext->plane]);
    xprint(uih->image, uih->font, 0, statusstart + 7 * h, str,
	   uih->encoding, FGCOLOR(uih), BGCOLOR(uih), 0);
    sprintf(str, gettext("incoloring:%s    outcoloring:%s"),
	    incolorname[uih->fcontext->incoloringmode],
	    outcolorname[uih->fcontext->coloringmode]);
    xprint(uih->image, uih->font, 0, statusstart + 8 * h, str,
	   uih->encoding, FGCOLOR(uih), BGCOLOR(uih), 0);
    sprintf(str, gettext("zoomspeed:%f"), (float) uih->maxstep * 1000);
    xprint(uih->image, uih->font, 0, statusstart + 9 * h, str,
	   uih->encoding, FGCOLOR(uih), BGCOLOR(uih), 0);
    if (uih->fcontext->mandelbrot)
	strcpy(str, gettext("Parameter:none"));
    else
	sprintf(str, gettext("Parameter:[%f,%f]"),
		(float) uih->fcontext->pre, (float) uih->fcontext->pim);
    xprint(uih->image, uih->font, 0, statusstart + 10 * h, str,
	   uih->encoding, FGCOLOR(uih), BGCOLOR(uih), 0);
#ifdef MEMCHECK
    {
	struct mallinfo i = mallinfo();
	sprintf(str, "Allocated arena:%i Wasted:%i %i", i.arena, i.ordblks,
		i.fordblks);
	xprint(uih->image, uih->font, 0, statusstart + 11 * h, str,
	       FGCOLOR(uih), BGCOLOR(uih), 0);
	sprintf(str, "Mmaped blocks%i Mmaped area:%i keep:%i", i.hblks,
		i.hblkhd, i.keepcost);
	xprint(uih->image, uih->font, 0, statusstart + 12 * h, str,
	       FGCOLOR(uih), BGCOLOR(uih), 0);
    }
#endif
    ui_flush();
}

static void ui_status(uih_context * uih)
{
    if (statuswindow == NULL) {
	statuswindow =
	    uih_registerw(uih, ui_statuspos, ui_drawstatus, NULL, 0);
    } else {
	uih_removew(uih, statuswindow);
	statuswindow = NULL;
    }
    ui_updatemenus(uih, "status");
    ui_updatemenus(uih, "animministatus");
    ui_updatestarts();
}

static int ui_statusenabled(uih_context * uih)
{
    return (statuswindow != NULL);
}

static void
ui_ministatuspos(uih_context * uih, int *x, int *y, int *w, int *h,
		 void *data)
{
    *x = 0;
    *y = ministatusstart;
    *w = uih->image->width;
    *h = xtextheight(uih->font);
}

static void ui_drawministatus(uih_context * uih, void *data)
{
    xprint(uih->image, uih->font, 0, ministatusstart, statustext,
	   uih->encoding, FGCOLOR(uih), BGCOLOR(uih), 0);
}

static void ui_noguisw(uih_context * uih)
{
    ui_nogui ^= 1;
    ui_updatemenus(uih, "nogui");
}

static int ui_noguienabled(uih_context * uih)
{
    return (ui_nogui);
}

static void ui_ministatus(uih_context * uih)
{
    if (ministatuswindow == NULL) {
	ministatuswindow =
	    uih_registerw(uih, ui_ministatuspos, ui_drawministatus, NULL,
			  0);
    } else {
	uih_removew(uih, ministatuswindow);
	ministatuswindow = NULL;
    }
    ui_updatestarts();
    ui_updatemenus(uih, "ministatus");
    ui_updatemenus(uih, "animministatus");
}

static int ui_ministatusenabled(uih_context * uih)
{
    return (ministatuswindow != NULL);
}

static void ui_message(struct uih_context *u)
{
    char s[80];
    if (uih->play)
	return;
    mousetype(WAITMOUSE);
    sprintf(s, gettext("Please wait while calculating %s"),
	    uih->fcontext->currentformula->name[!uih->fcontext->
						mandelbrot]);
    driver->print(0, 0, s);
}

#define ROTATESPEEDUP 30
static int
procescounter(int *counter, CONST char *text, int speed, int keys,
	      int lastkeys, int down, int up, int tenskip, int min,
	      int max)
{
    static int pid = -1;
    int changed = 0;
    char str[80];
    if (tl_lookup_timer(arrowtimer) > 1000000)
	tl_reset_timer(arrowtimer);
    if ((keys & up) && !(lastkeys & up)) {
	(*counter)++;
	tenskip = 0;
	changed = 1;
	tl_reset_timer(arrowtimer);
    }
    if ((keys & down) && !(lastkeys & down)) {
	(*counter)--;
	tenskip = 0;
	changed = 1;
	tl_reset_timer(arrowtimer);
    }
    while (tl_lookup_timer(arrowtimer) > speed * FRAMETIME) {
	tl_slowdown_timer(arrowtimer, speed * FRAMETIME);
	if (keys & up) {
	    if (tenskip && !(*counter % 10))
		(*counter) += 10;
	    else
		(*counter)++;
	    changed = 1;
	}
	if (keys & down) {
	    if (tenskip && !(*counter % 10))
		(*counter) -= 10;
	    else
		(*counter)--;
	    changed = 1;
	}
    }
    if (changed) {
	if (*counter > max)
	    *counter = max;
	if (*counter < min)
	    *counter = min;
	sprintf(str, text, *counter);
	uih_rmmessage(uih, pid);
	pid = uih_message(uih, str);
	ui_flush();
    }
    return changed;
}

static void
ui_mouse(int mousex, int mousey, int mousebuttons, int iterchange)
{
    int flags;
    char str[80];
    static int spid;
    flags = 0;
    if (mousex != lastx || mousey != lasty)
	flags |= MOUSE_MOVE;
    if ((mousebuttons & BUTTON1) && !(lastbuttons & BUTTON1))
	flags |= MOUSE_PRESS;
    if (!(mousebuttons & BUTTON1) && (lastbuttons & BUTTON1))
	flags |= MOUSE_RELEASE;
    if (mousebuttons & BUTTON1)
	flags |= MOUSE_DRAG;
    lastx = mousex;
    lasty = mousey;
    lastbuttons = mousebuttons;
    tl_update_time();
    CHECKPROCESSEVENTS(mousebuttons, iterchange);
    if (ui_helpmouse(mousex, mousey, mousebuttons, flags)) {
	uih_update(uih, mousex, mousey, 0);
	return;
    }
#ifndef _plan9_
    if (ui_mousefilesel(mousex, mousey, mousebuttons, flags)) {
	uih_update(uih, mousex, mousey, 0);
	return;
    }
#endif
    if (ui_dialogmouse(mousex, mousey, mousebuttons, flags)) {
	uih_update(uih, mousex, mousey, 0);
	return;
    }
    if (ui_menumouse(mousex, mousey, mousebuttons, flags)) {
	uih_update(uih, mousex, mousey, 0);
	return;
    }
    uih_update(uih, mousex, mousey, mousebuttons);
    if (uih->play) {
	procescounter(&uih->letterspersec,
		      gettext("Letters per second %i  "), 2, iterchange,
		      lastiter, 1, 2, 0, 1, INT_MAX);
	return;
    }
    if (!uih->cycling) {
	if (uih->rotatemode == ROTATE_CONTINUOUS) {
	    static int rpid;
	    if (iterchange == 2) {
		uih->rotationspeed +=
		    ROTATESPEEDUP * tl_lookup_timer(maintimer) / 1000000.0;
		uih_rmmessage(uih, rpid);
		sprintf(str,
			gettext
			("Rotation speed:%2.2f degrees per second "),
			(float) uih->rotationspeed);
		rpid = uih_message(uih, str);
		ui_flush();
	    }
	    if (iterchange == 1) {
		uih->rotationspeed -=
		    ROTATESPEEDUP * tl_lookup_timer(maintimer) / 1000000.0;
		uih_rmmessage(uih, rpid);
		sprintf(str,
			gettext
			("Rotation speed:%2.2f degrees per second "),
			(float) uih->rotationspeed);
		rpid = uih_message(uih, str);
		ui_flush();
	    }
	    tl_reset_timer(maintimer);
	} else {
	    if (!dirty)
		maxiter = uih->fcontext->maxiter;
	    if (procescounter
		(&maxiter, gettext("Iterations: %i   "), 1, iterchange,
		 lastiter, 1, 2, 1, 1, INT_MAX) || (iterchange & 3)) {
		dirty = 1;
		lastiter = iterchange;
		return;
	    }
	}
    }
    if (dirty) {
	if (uih->incalculation)
	    uih_interrupt(uih);
	else
	    uih_setmaxiter(uih, maxiter), dirty = 0;
    }
    if (uih->cycling) {
	if (procescounter
	    (&uih->cyclingspeed, gettext("Cycling speed: %i   "), 1,
	     iterchange, lastiter, 1, 2, 0, -1000000, INT_MAX)) {
	    uih_setcycling(uih, uih->cyclingspeed);
	}
    }
    if (iterchange & 4
	&& (tl_lookup_timer(maintimer) > FRAMETIME || mousebuttons)) {
	double mul1 = tl_lookup_timer(maintimer) / FRAMETIME;
	double su = 1 + (SPEEDUP - 1) * mul1;
	if (su > 2 * SPEEDUP)
	    su = SPEEDUP;
	tl_reset_timer(maintimer);
	uih->speedup *= su, uih->maxstep *= su;
	sprintf(str, gettext("speed:%2.2f "),
		(double) uih->speedup * (1.0 / STEP));
	uih_rmmessage(uih, spid);
	spid = uih_message(uih, str);
	ui_flush();
    }
    if (iterchange & 8
	&& (tl_lookup_timer(maintimer) > FRAMETIME || mousebuttons)) {
	double mul1 = tl_lookup_timer(maintimer) / FRAMETIME;
	double su = 1 + (SPEEDUP - 1) * mul1;
	if (su > 2 * SPEEDUP)
	    su = SPEEDUP;
	tl_reset_timer(maintimer);
	uih->speedup /= su, uih->maxstep /= su;
	sprintf(str, gettext("speed:%2.2f "),
		(double) uih->speedup * (1 / STEP));
	uih_rmmessage(uih, spid);
	spid = uih_message(uih, str);
	ui_flush();
    }
    lastiter = iterchange;
    return;
}

void ui_call_resize(void)
{
    callresize = 1;
    uih_interrupt(uih);
}

static int
ui_alloccolor(struct palette *pal, int init, int r, int g, int b)
{
    int i;
    i = driver->set_color(r, g, b, init);
    if (i == -1)
	return (-1);
    if (init)
	pal->size = 0;
    pal->pixels[pal->size] = i;
    pal->rgb[i][0] = r;
    pal->rgb[i][1] = g;
    pal->rgb[i][2] = b;
    pal->size++;
    if (driver->flags & UPDATE_AFTER_PALETTE) {
	uih->display = 1;
    }
    return (i);
}

static void
ui_setpalette(struct palette *pal, int start, int end, rgb_t * rgb1)
{
    driver->set_range((ui_palette) rgb1, start, end);
}

static void ui_flip(struct image *image)
{
    flipgeneric(image);
    driver->flip_buffers();
}

static int ui_driverselected(uih_context * c, int d)
{
    return (driver == drivers[d]);
}

static void ui_setdriver(uih_context * c, int d)
{
    todriver = d + 1;
}

static void processbuffer(void)
{
    CONST menuitem *item;
    dialogparam *d;
    if (uih->incalculation)
	return;
    while ((item = menu_delqueue(&d)) != NULL) {
	ui_menuactivate(item, d);
    }
}

static void ui_doquit(int i) NORETURN;
static void ui_doquit(int i)
{
    uih_cycling_off(uih);
    uih_freecatalog(uih);
    uih_freecontext(uih);
    tl_free_timer(maintimer);
    tl_free_timer(arrowtimer);
    tl_free_timer(loopt);
    driver->free_buffers(NULL, NULL);
    driver->uninit();
    destroypalette(image->palette);
    destroy_image(image);
    xth_uninit();
    xio_uninit();
    ui_unregistermenus();
    uih_unregistermenus();
    exit_xaos(i);
}

void ui_quit(void)
{
#ifndef _MAC
    printf(gettext("Thank you for using XaoS\n"));
#endif
    ui_doquit(0);
}

static void ui_quitwr(uih_context * c, int quit)
{
    if (c == NULL) {
	ui_unregistermenus();
	uih_unregistermenus();
	xio_uninit();
	exit_xaos(0);
    }
    if (quit)
	ui_quit();
}

int ui_key(int key)
{
    int sym;
    char mkey[2];
    CONST menuitem *item;
#ifdef _plan9_
#define ui_keyfilesel(k) 0
#endif
    if (!ui_helpkeys(key) && !ui_keyfilesel(key) && !ui_dialogkeys(key)
	&& !ui_menukey(key))
	switch (sym = tolower(key)) {
	case ' ':
	    ui_closemenus();
	    uih->display = 1;
	    if (uih->play) {
		if (uih->incalculation)
		    ui_updatestatus();
		else {
		    uih_skipframe(uih);
		    driver->print(0, 0,
				  gettext("Skipping, please wait..."));
		}
	    }
	    break;
	default:
	    {
		int number;
		if (sym >= '0' && sym <= '9') {
		    number = sym - '1';
		    if (number < 0)
			number = 9;
		    if (number == -1)
			break;
		}
	    }
	    mkey[0] = key;
	    mkey[1] = 0;
	    item = menu_findkey(mkey, uih->menuroot);
	    if (item == NULL) {
		mkey[0] = sym;
		item = menu_findkey(mkey, uih->menuroot);
	    }
	    if (item != NULL) {
		dialogparam *p = NULL;
		if (menu_havedialog(item, uih)) {
		    CONST menudialog *d = menu_getdialog(uih, item);
		    int mousex, mousey, buttons;
		    driver->getmouse(&mousex, &mousey, &buttons);
		    if (d[0].question != NULL && d[1].question == NULL
			&& d[0].type == DIALOG_COORD) {
			p = (dialogparam *) malloc(sizeof(dialogparam));
			uih_screentofractalcoord(uih, mousex, mousey,
						 p->dcoord, p->dcoord + 1);
		    }
		}
		ui_menuactivate(item, p);
	    }
	    break;
	}
    processbuffer();
    return 0;
}

#ifdef _EFENCE_
int EF_ALIGNMENT = 1;
int EF_PROTECT_BELOW = 0;
int EF_PROTECT_FREE = 1;
#endif
static void ui_helpwr(struct uih_context *c)
{
    ui_help("main");
}

char *ui_getpos(void)
{
    return (uih_savepostostr(uih));
}

void ui_loadstr(CONST char *n)
{
    uih_loadstr(uih, n);
}

static menuitem *menuitems;
/* This structure is now empty. All static definitions have been moved
   to ui_registermenus_i18n() which fills up its own static array. */

/* Registering internationalized menus. See also include/xmenu.h
   for details. Note that MAX_MENUITEMS_I18N may be increased
   if more items will be added in future. */

/* Details of implementation:
 *
 * There are static menuitems_i18n[] arrays for several *.c files.
 * In these files this array is common for all functions.
 * Here, e.g. add_resizeitems() and ui_registermenus_i18n()
 * use the same array and ui_no_menuitems_i18n is the counter
 * that counts the number of items. The local variables
 * count the local items.
 */

#define MAX_MENUITEMS_I18N 20
/* These variables must be global: */
static menuitem menuitems_i18n[MAX_MENUITEMS_I18N];
int ui_no_menuitems_i18n = 0, ui_no_resizeitems;
static menuitem *resizeitems;

#define UI (MENUFLAG_NOPLAY|MENUFLAG_NOOPTION)
static void add_resizeitems()
{
    // General version, it's needed now:
    int no_menuitems_i18n = ui_no_menuitems_i18n;	/* This variable must be local. */
    MENUNOP_I("ui", "=", gettext("Resize"), "resize",
	      UI | MENUFLAG_INTERRUPT, ui_call_resize);
    MENUNOP_I("uia", "=", gettext("Resize"), "animresize",
	      UI | MENUFLAG_INTERRUPT, ui_call_resize);
    no_menuitems_i18n -= ui_no_menuitems_i18n;
    resizeitems = &menuitems_i18n[ui_no_menuitems_i18n];
    menu_add(resizeitems, no_menuitems_i18n);
    ui_no_resizeitems = no_menuitems_i18n;
    ui_no_menuitems_i18n += no_menuitems_i18n;
}

static void ui_registermenus_i18n(void)
{
    int no_menuitems_i18n = ui_no_menuitems_i18n;	/* This variable must be local. */
#ifndef MACOSX
    SUBMENU_I("file", "q", gettext("Quit"), "quitmenu");
    MENUINT_I("quitmenu", NULL, gettext("Exit now"), "quit",
	      MENUFLAG_INTERRUPT | MENUFLAG_ATSTARTUP, ui_quitwr, 1);
    MENUINT_I("quitmenu", NULL, gettext("Not yet"), "noquit", UI,
	      ui_quitwr, 0);
#endif
    MENUNOP_I("helpmenu", "h", gettext("Help"), "help", MENUFLAG_INCALC,
	      ui_helpwr);
    MENUNOPCB_I("ui", NULL, gettext("Disable XaoS's builtin GUI"), "nogui",
		MENUFLAG_INCALC | MENUFLAG_ATSTARTUP | MENUFLAG_NOMENU,
		ui_noguisw, ui_noguienabled);
    MENUSEPARATOR_I("ui");
    MENUNOPCB_I("ui", "/", gettext("Status"), "status", MENUFLAG_INCALC, ui_status, ui_statusenabled);	/*FIXME: add also ? as key */

    MENUNOPCB_I("ui", "l", gettext("Ministatus"), "ministatus",
		MENUFLAG_INCALC, ui_ministatus, ui_ministatusenabled);
    MENUSEPARATOR_I("ui");
    MENUSEPARATOR_I("uia");
    MENUNOPCB_I("uia", "/", gettext("Status"), "animstatus", UI | MENUFLAG_INCALC, ui_status, ui_statusenabled);	/*FIXME: add also ? as key */

    MENUNOPCB_I("uia", "l", gettext("Ministatus"), "animministatus",
		UI | MENUFLAG_INCALC, ui_ministatus, ui_ministatusenabled);
    MENUSEPARATOR_I("uia");
    SUBMENU_I("ui", NULL, gettext("Driver"), "drivers");
    SUBMENU_I("uia", NULL, gettext("Driver"), "drivers");
    no_menuitems_i18n -= ui_no_menuitems_i18n;
    menu_add(&(menuitems_i18n[ui_no_menuitems_i18n]), no_menuitems_i18n);
    ui_no_menuitems_i18n += no_menuitems_i18n;
}

/* Registering driver items: */
static menuitem *driveritems;
static void ui_registermenus(void)
{
    int i;
    menuitem *item;
    menu_add(menuitems, NITEMS(menuitems));
    driveritems = item = (menuitem *) malloc(sizeof(menuitem) * ndrivers);
    for (i = 0; i < ndrivers; i++) {
	item[i].menuname = "drivers";
	item[i].shortname = drivers[i]->name;
	item[i].key = NULL;
	item[i].type = MENU_INT;
	item[i].flags = MENUFLAG_RADIO | UI;
	item[i].iparam = i;
	item[i].name = drivers[i]->name;
	item[i].function = (void (*)(void)) ui_setdriver;
	item[i].control = (int (*)(void)) ui_driverselected;
    }
    menu_add(item, ndrivers);
}

static void ui_unregistermenus(void)
{
    menu_delete(menuitems, NITEMS(menuitems));
    menu_delete(driveritems, ndrivers);
    menu_delete(menuitems_i18n, ui_no_menuitems_i18n);
    free(driveritems);
}

int number_six = 6;

#ifdef SFFE_USING
	/* parser variables vars */
cmplx Z, C, pZ;
#endif

#define MAX_WELCOME 50
#ifndef MAIN_FUNCTION
#define MAIN_FUNCTION main
#endif
#ifdef __cplusplus
extern "C" {
    int MAIN_FUNCTION(int argc, char **argv);
}
#endif
int MAIN_FUNCTION(int argc, char **argv)
{
    int i;
    int width, height;
    char welcome[MAX_WELCOME], language[11];
#ifdef HAVE_GETTEXT
    char *locale;
#endif
#ifdef DESTICKY
    euid = geteuid();
    egid = getegid();
#endif
#ifdef DESTICKY
    seteuid(getuid());		/* Don't need supervisor rights anymore. */
    setegid(getgid());
#endif

    strcpy(language, "english");
#ifdef HAVE_GETTEXT
    /* Setting all locales for XaoS: */
    locale = setlocale(LC_ALL, "");
    if (locale == NULL) {
	printf
	    ("An error occured in your setlocale/gettext installation.\n");
	printf("I18n menus will not be available.\n");
    }
#ifdef _WIN32
    // x_message("%s",locale);
    if (locale != NULL) {
	if (strncmp(locale, "Hungarian", 9) == 0)
	    strcpy(language, "magyar");
	if (strncmp(locale, "Czech", 5) == 0)
	    strcpy(language, "cesky");
	if (strncmp(locale, "German", 6) == 0)
	    strcpy(language, "deutsch");
	if (strncmp(locale, "Spanish", 8) == 0)
	    strcpy(language, "espanhol");
	if (strncmp(locale, "French", 6) == 0)
	    strcpy(language, "francais");
	if (strncmp(locale, "Romanian", 8) == 0)
	    strcpy(language, "romanian");
	if (strncmp(locale, "Italian", 7) == 0)
	    strcpy(language, "italiano");
	if (strncmp(locale, "Portuguese", 10) == 0)
	    strcpy(language, "portuguese");
    }
    // x_message("%s",language);
#else
    if ((locale == NULL) || (strcmp(locale, "C") == 0))
	locale = getenv("LANG");
    else
	locale = setlocale(LC_MESSAGES, "");


#ifdef DEBUG
    printf("Trying to use locale settings for %s.\n", locale);
#endif

    if (locale != NULL) {
	if (strlen(locale) > 2)
	    locale[2] = '\0';
	if (strcmp(locale, "hu") == 0)
	    strcpy(language, "magyar");
	if (strcmp(locale, "cs") == 0)
	    strcpy(language, "cesky");
	if (strcmp(locale, "de") == 0)
	    strcpy(language, "deutsch");
	if (strcmp(locale, "es") == 0)
	    strcpy(language, "espanhol");
	if (strcmp(locale, "fr") == 0)
	    strcpy(language, "francais");
	if (strcmp(locale, "ro") == 0)
	    strcpy(language, "romanian");
	if (strcmp(locale, "it") == 0)
	    strcpy(language, "italiano");
	if (strcmp(locale, "pt") == 0)
	    strcpy(language, "portuguese");
    }
#endif
#ifdef DEBUG
    printf("Using catalog file for %s language.\n", language);
#endif
    /* Without this some locales (e.g. the Hungarian) replaces "." to ","
       in numerical format and this will cause an automatic truncation
       at each parameter at certain places, e.g. drawing a new fractal. */
    setlocale(LC_NUMERIC, "C");
#ifdef DEBUG
    printf("Text domain will be bound to directory %s.\n",
#endif
	   bindtextdomain("xaos",
#ifdef DOG_DRIVER
			  "..\\locale")
#ifdef DEBUG
	)
#endif
#else
#ifdef _WIN32
			  "..\\locale")
#ifdef DEBUG
	)
#endif
#else
#ifdef USE_LOCALEPATH
			  localepath)
#else
			  "/usr/share/locale")
#endif
#ifdef DEBUG
	)
#endif
#endif
#endif
	;
#ifndef _WIN32
    bind_textdomain_codeset("xaos", "UTF-8");
#endif
    textdomain("xaos");
    /* Done setting locales. */
#endif
    xio_init(argv[0]);
    params_register(global_params);
    params_register(ui_fractal_params);
    uih_registermenudialogs_i18n();	/* Internationalized dialogs. */
    /* Dialogs must be generated before menus because menu items
       link to dialog pointers. */
    uih_registermenus_i18n();	/* Internationalized menus. */
    uih_registermenus();
    ui_registermenus();
    ui_registermenus_i18n();	/* Internationalized menus. */
    for (i = 0; i < ndrivers; i++)
	params_register(drivers[i]->params);
#ifdef __alpha__
#ifdef __linux__
    extern void ieee_set_fp_control(unsigned long);
    ieee_set_fp_control(1UL);
#endif
#endif
    prog_argc = argc;
    prog_argv = argv;
    if (!params_parser(argc, argv)) {
	ui_unregistermenus();
	uih_unregistermenus();
	xio_uninit();
	exit_xaos(-1);
    }
#ifdef MEM_DEBUG
    D_NORMAL;
#endif
#ifdef DEBUG
    printf("Initializing driver\n");
#endif
#ifndef __BEOS__
#ifndef _plan9_
    signal(SIGFPE, SIG_IGN);
#endif
#endif
    if (printconfig) {
#define tostring(s) #s
	x_message("XaoS configuration\n"
		  "Version:   %s\n"
		  "Type size: %i\n" "integer size: %i\n" "configfile: %s\n"
#ifndef _plan9_
#ifdef HAVE_ALLOCA
		  "using alloca\n"
#endif
#ifdef HAVE_LONG_DOUBLE
		  "using long double\n"
#endif
#ifdef const
		  "const disabled\n"
#endif
#ifdef inline
		  "inline disabled\n"
#endif
#ifdef HAVE_GETTIMEOFDAY
		  "using gettimeofday\n"
#endif
#ifdef HAVE_FTIME
		  "using ftime\n"
#endif
#ifdef MITSHM
		  "using mitshm\n"
#endif
#ifdef HAVE_MOUSEMASK
		  "using ncurses mouse\n"
#endif
#ifdef DEBUG
		  "debug enabled\n"
#endif
#ifdef NDEBUG
		  "assertions disabled\n"
#endif
#ifdef STATISTICS
		  "statistics enabled\n"
#endif
#ifdef SFFE_USING
		  "user formula evaluation\n"
#endif
#endif
		  , XaoS_VERSION, (int) sizeof(FPOINT_TYPE),
		  (int) sizeof(int), CONFIGFILE);
    }
    if (deflist || printconfig) {
	char s[256];
	strcpy(s, "Available drivers: ");
	for (i = 0; i < ndrivers; i++) {
	    strcat(s, drivers[i]->name);
	    if (i < ndrivers - 1)
		strcat(s, ", ");
	}
	x_message(s);
	ui_unregistermenus();
	uih_unregistermenus();
	xio_uninit();
	exit_xaos(0);
    }
#ifndef _plan9_
    xth_init(defthreads);
#endif
    {
	int i = ui_dorender_params();
	if (i) {
	    ui_unregistermenus();
	    uih_unregistermenus();
	    xio_uninit();
	    exit_xaos(i - 1);
	}
    }
    if (defdriver != NULL) {
	for (i = 0; i < ndrivers; i++) {
	    int y;
	    for (y = 0;
		 tolower(drivers[i]->name[y]) == tolower(defdriver[y])
		 && drivers[i]->name[y] != 0; y++);
	    if (drivers[i]->name[y] == 0) {
		driver = drivers[i];
		if (driver->init())
		    break;
		else {
		    x_fatalerror("Can not initialize %s driver",
				 defdriver);
		}
	    }
	}
	if (i == ndrivers) {
	    x_fatalerror("Unknown driver %s", defdriver);
	}
    } else {
	for (i = 0; i < ndrivers; i++) {
	    driver = drivers[i];
	    if (driver->init())
		break;
	}
	if (i == ndrivers) {
	    x_fatalerror("Can not initialize driver");
	}
    }
#ifdef DEBUG
    printf("Getting size\n");
#endif
    driver->getsize(&width, &height);
#ifdef _plan9_
    xth_init(defthreads);	/*plan0 requires to initialize tasks after graphics */
#endif
    mousetype(WAITMOUSE);
    driver->print(0, 0, "Initializing. Please wait");
    driver->print(0, textheight1, "Creating framebuffer");
    ui_flush();
    ui_mkimages(width, height);

    driver->print(0, textheight1 * 2, "Initializing fractal engine");
    ui_flush();

    /* gloabuih initialization moved into uih_mkcontext function : malczak */
    uih =
	uih_mkcontext(driver->flags, image, ui_passfunc, ui_message,
		      ui_updatemenus);

    if (driver->gui_driver && driver->gui_driver->setrootmenu)
	driver->gui_driver->setrootmenu(uih, uih->menuroot);
    ui_flush();
#ifdef HOMEDIR
    if (getenv("HOME") != NULL) {
	char home[256], *env = getenv("HOME");
	int maxsize = 255 - (int) strlen(CONFIGFILE) - 1;	/*Avoid buffer owerflow */
	int i;
	for (i = 0; i < maxsize && env[i]; i++)
	    home[i] = env[i];
	home[i] = 0;
	xio_addfname(configfile, home, CONFIGFILE);
    } else
#endif
	xio_addfname(configfile, XIO_EMPTYPATH, CONFIGFILE);
    ui_flush();
    srand(time(NULL));
    uih->fcontext->version++;
    maintimer = tl_create_timer();
    arrowtimer = tl_create_timer();
    loopt = tl_create_timer();
    driver->print(0, textheight1 * 3, "Loading message catalog");
    ui_flush();
    uih_loadcatalog(uih, language);
    driver->print(0, textheight1 * 4, "Initializing timming system");
    ui_flush();
    uih_newimage(uih);
    tl_update_time();
    /*tl_process_group (syncgroup, NULL); */
    tl_reset_timer(maintimer);
    tl_reset_timer(arrowtimer);
#ifdef COMPILE_PIPE
    if (defpipe != NULL) {
	driver->print(0, textheight1 * 5, "Initializing pipe");
	ui_flush();
	ui_pipe_init(defpipe);
    }
#else
    if (defpipe != NULL) {
	x_fatalerror("Pipe input not supported!");
    }
#endif
    /*uih_constantframetime(uih,1000000/20); */
    driver->print(0, textheight1 * 6, "Reading configuration file");
    {
	xio_file f = xio_ropen(configfile);	/*load the configuration file */
	if (f != XIO_FAILED) {
	    uih_load(uih, f, configfile);
	    if (uih->errstring) {
		x_error("Configuration file %s load failed", configfile);
		uih_printmessages(uih);
		x_error("Hint:try to remove it :)");
		ui_doquit(1);
	    }
	}
    }
    driver->print(0, textheight1 * 7,
		  "Processing command line parameters");
    ui_flush();
    {
	CONST menuitem *item;
	dialogparam *d;
	while ((item = menu_delqueue(&d)) != NULL) {
	    uih_saveundo(uih);
	    menu_activate(item, uih, d);
	}
    }
#ifndef _plan9_
    sprintf(welcome, gettext("Welcome to XaoS version %s"), XaoS_VERSION);
    /*TYPE*/ uih_message(uih, welcome);
#endif
    uih_updatemenus(uih, driver->name);
    if (printspeed) {
	int c = 0;
	int x, y, b, k;
	int linesize = uih->image->bytesperpixel * uih->image->height;
	int size = linesize * uih->image->height;
	driver->print(0, textheight1 * 8, "Preparing for speedtest");
	ui_flush();
	uih->passfunc = NULL;
	tl_sleep(1000000);
	for (c = 0; c < 5; c++)
	    driver->display(), ui_flush();
	driver->processevents(0, &x, &y, &b, &k);
	driver->print(0, textheight1 * 9, "Measuring dislay speed");
	ui_flush();
	tl_sleep(1000000);
	tl_update_time();
	tl_reset_timer(maintimer);
	c = 0;
	while (tl_lookup_timer(maintimer) < 5000000)
	    driver->display(), ui_flush(), driver->processevents(0, &x, &y,
								 &b, &k),
		tl_update_time(), c++;
	x_message("Driver speed: %g FPS (%.4f MBPS)", c / 5.0,
		  c * (double) size / 5.0 / 1024 / 1024);

	driver->print(0, textheight1 * 10, "Measuring memcpy speed");
	ui_flush();
	for (c = 0; c < 5; c++) {
	    for (x = 0; x < uih->image->height; x++)
		memcpy(uih->image->currlines[y], uih->image->oldlines[y],
		       linesize);
	}
	tl_update_time();
	tl_reset_timer(maintimer);
	c = 0;
	while (tl_lookup_timer(maintimer) < 5000000) {
	    for (x = 0; x < uih->image->height; x++)
		memcpy(uih->image->currlines[y], uih->image->oldlines[y],
		       linesize);
	    tl_update_time(), c++;
	}
	x_message("Memcpy speed: %g FPS (%.4f MBPS)", c / 5.0,
		  c * (double) size / 5.0 / 1024 / 1024);

	driver->print(0, textheight1 * 10,
		      "Measuring missaligned memcpy speed");
	tl_update_time();
	tl_reset_timer(maintimer);
	c = 0;
	while (tl_lookup_timer(maintimer) < 5000000) {
	    for (x = 0; x < uih->image->height; x++)
		memcpy(uih->image->currlines[y] + 1,
		       uih->image->oldlines[y] + 2, linesize - 2);
	    tl_update_time(), c++;
	}
	x_message("Missaligned memcpy speed: %g FPS (%.4f MBPS)", c / 5.0,
		  c * (double) size / 5.0 / 1024 / 1024);

	driver->print(0, textheight1 * 10, "Measuring size6 memcpy speed");
	tl_update_time();
	tl_reset_timer(maintimer);
	c = 0;
	while (tl_lookup_timer(maintimer) < 5000000) {
	    int x, y;
	    for (y = 0; y < uih->image->height; y++)
		for (x = 0; x < linesize - 6; x += 6) {
		    memcpy(uih->image->currlines[y] + x,
			   uih->image->oldlines[y] + x, number_six);
		}
	    tl_update_time(), c++;
	}
	x_message("Size 6 memcpy speed: %g FPS (%.4f MBPS)", c / 5.0,
		  c * (double) size / 5.0 / 1024 / 1024);

	driver->display();
	driver->print(0, textheight1 * 11, "Measuring calculation speed");
	ui_flush();
	speed_test(uih->fcontext, image);
	driver->print(0, textheight1 * 12,
		      "Measuring new image calculation loop");
	ui_flush();
	uih_prepare_image(uih);
	tl_update_time();
	tl_reset_timer(maintimer);
	for (c = 0; c < 5; c++)
	    uih_newimage(uih), uih->fcontext->version++,
		uih_prepare_image(uih);
	driver->display();
	ui_flush();
	x_message("New image caluclation took %g seconds (%.2g fps)",
		  tl_lookup_timer(maintimer) / 5.0 / 1000000.0,
		  5000000.0 / tl_lookup_timer(maintimer));
	tl_update_time();
	for (c = 0; c < 5; c++)
	    uih_animate_image(uih), uih_prepare_image(uih), c++;
	c = 0;
	tl_update_time();
	tl_reset_timer(maintimer);
	driver->print(0, textheight1 * 13,
		      "Measuring zooming algorithm loop");
	ui_flush();
	while (tl_lookup_timer(maintimer) < 5000000)
	    uih_animate_image(uih), uih_prepare_image(uih),
		tl_update_time(), c++;
	x_message("Approximation loop speed: %g FPS", c / 5.0);
	ui_doquit(0);
    }
#ifdef SFFE_USING
    /*SFFE : malczak */
    if (uih->parser->expression == NULL)
	if (sffeform)
	    err = sffe_parse(&uih->parser, (char *) sffeform);
	else
	    sffe_parse(&uih->parser, "z^2+c");

    if (sffeinit) {
	uih->pinit = sffe_alloc();
	sffe_regvar(&uih->pinit, &pZ, 'p');
	sffe_regvar(&uih->pinit, &C, 'c');
	if (sffe_parse(&uih->pinit, (char *) sffeinit) > 0)
	    sffe_free(&uih->pinit);
    };

    if (err > 0)
	sffe_parse(&uih->parser, "z^2+c");
     /*SFFE*/
#endif
	driver->print(0, textheight1 * 8, "Entering main loop");
    ui_flush();
    main_loop();
    ui_quit();

    return (0);
}

static void ui_mkimages(int w, int h)
{
    struct palette *palette;
    int scanline;
    int width, height;
    union paletteinfo info;
    char *b1, *b2;
    width = w;
    height = h;
    if (resizeregistered && !(driver->flags & RESIZE_COMMAND)) {
	menu_delete(resizeitems, ui_no_resizeitems);
	resizeregistered = 0;
    } else {
	if (!resizeregistered && (driver->flags & RESIZE_COMMAND)) {
	    add_resizeitems();
	    resizeregistered = 1;
	}
    }
    if (!(scanline = driver->alloc_buffers(&b1, &b2))) {
	driver->uninit();
	x_error(gettext("Can not allocate buffers"));
	ui_outofmem();
	exit_xaos(-1);
    }
    info.truec.rmask = driver->rmask;
    info.truec.gmask = driver->gmask;
    info.truec.bmask = driver->bmask;
    palette =
	createpalette(driver->palettestart, driver->paletteend,
		      driver->imagetype,
		      (driver->
		       flags & RANDOM_PALETTE_SIZE) ? UNKNOWNENTRIES : 0,
		      driver->maxentries,
		      driver->set_color != NULL ? ui_alloccolor : NULL,
		      driver->set_range != NULL ? ui_setpalette : NULL,
		      NULL, NULL, &info);
    if (!palette) {
	driver->uninit();
	x_error(gettext("Can not create palette"));
	ui_outofmem();
	exit_xaos(-1);
    }
    image =
	create_image_cont(width, height, scanline, 2, (unsigned char *) b1,
			  (unsigned char *) b2, palette, ui_flip,
			  (driver->flags & AALIB) ? AAIMAGE : 0,
			  get_windowwidth(width) / width,
			  get_windowheight(height) / height);
    if (!image) {
	driver->uninit();
	x_error(gettext("Can not create image"));
	ui_outofmem();
	exit_xaos(-1);
    }
}

void ui_resize(void)
{
    int w, h;
    if (uih->incalculation) {
	uih_interrupt(uih);
	return;
    }
    ui_closemenus();
    ui_closedialog(0);
    ui_close_help();
    uih_clearwindows(uih);
    uih_stoptimers(uih);
    uih_cycling_stop(uih);
    uih_savepalette(uih);
    driver->getsize(&w, &h);
    assert(w > 0 && w < 65000 && h > 0 && h < 65000);
    if (w != uih->image->width || h != uih->image->height
	|| (driver->flags & UPDATE_AFTER_RESIZE)
	|| uih->palette->type != driver->imagetype) {
	driver->free_buffers(NULL, NULL);
	destroy_image(uih->image);
	destroypalette(uih->palette);
	ui_mkimages(w, h);
	if (!uih_updateimage(uih, image)) {
	    driver->uninit();
	    x_error(gettext("Can not allocate tables"));
	    ui_outofmem();
	    exit_xaos(-1);
	}
	tl_process_group(syncgroup, NULL);
	tl_reset_timer(maintimer);
	tl_reset_timer(arrowtimer);
	uih_newimage(uih);
    }
    uih_newimage(uih);
    uih_restorepalette(uih);
    /*uih_mkdefaultpalette(uih); */
    uih->display = 1;;
    uih_cycling_continue(uih);
}

static void ui_driver(int d)
{
    CONST struct ui_driver *driver1;
    int width, height;
    ui_closemenus();
    ui_closedialog(0);
    ui_close_help();
    if (d < 0)
	d = 0;
    if (d >= ndrivers)
	d = ndrivers - 1;
    uih_stoptimers(uih);
    driver1 = driver;
    uih_clearwindows(uih);
    uih_cycling_off(uih);
    uih_savepalette(uih);
    driver->free_buffers(NULL, NULL);
    driver->uninit();
    driver = drivers[d];
    if (!driver->init()) {
	driver = driver1;
	uih_error(uih, gettext("Can not initialize driver"));
	if (!driver1->init()) {
	    x_fatalerror(gettext
			 ("Can not return back to previous driver"));
	} else
	    driver = driver1;
    }
    driver->getsize(&width, &height);
    destroy_image(uih->image);
    destroypalette(uih->palette);
    uih->flags = driver->flags;
    ui_mkimages(width, height);
    if (!uih_updateimage(uih, image)) {
	driver->uninit();
	x_error(gettext("Can not allocate tables"));
	ui_outofmem();
	exit_xaos(-1);
    }
    if (driver->gui_driver && driver->gui_driver->setrootmenu)
	driver->gui_driver->setrootmenu(uih, uih->menuroot);
    tl_process_group(syncgroup, NULL);
    tl_reset_timer(maintimer);
    tl_reset_timer(arrowtimer);
    uih->display = 1;
    uih_newimage(uih);
    uih_restorepalette(uih);
    ui_updatestatus();
    uih_updatemenus(uih, driver->name);
}

NORETURN static void main_loop(void)
{
    int inmovement = 1;
    int x, y, b, k;
    int time;
    driver->processevents((!inmovement && !uih->inanimation), &x, &y, &b,
			  &k);
    while (1) {
	mousetype(uih->play ? REPLAYMOUSE : uih->
		  inhibittextoutput ? VJMOUSE : NORMALMOUSE);
	if (uih->display) {
	    uih_prepare_image(uih);
	    ui_updatestatus();
	}
	if ((time = tl_process_group(syncgroup, NULL)) != -1) {
	    if (!inmovement && !uih->inanimation) {
		if (time > 1000000 / 50)
		    time = 1000000 / 50;
		if (time > delaytime) {
		    tl_sleep(time - delaytime);
		    tl_update_time();
		}
	    }
	    inmovement = 1;
	}
	if (delaytime || maxframerate) {
	    tl_update_time();
	    time = tl_lookup_timer(loopt);
	    tl_reset_timer(loopt);
	    time = 1000000 / maxframerate - time;
	    if (time < delaytime)
		time = delaytime;
	    if (time) {
		tl_sleep(time);
		tl_update_time();
	    }
	}
	processbuffer();
	driver->processevents((!inmovement && !uih->inanimation), &x, &y,
			      &b, &k);

	inmovement = 0;
	ui_mouse(x, y, b, k);
	if (todriver)
	    ui_driver(todriver - 1), todriver = 0;
	if (callresize)
	    ui_resize(), callresize = 0;
    }
}
