/*
	GIER interface (Motif)

	(C) Copyright 2001 by Mogens Kjaer


   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/Label.h>
#include <Xm/PushB.h>
#include <Xm/MainW.h>
#include <Xm/RowColumn.h>
#include <Xm/MessageB.h>
#include <Xm/FileSB.h>
#include <Xm/DialogS.h>
#include <Xm/Separator.h>
#include <Xm/ToggleB.h>
#include <Xm/Protocols.h>
#include <Xm/DrawingA.h>
#include <Xm/ScrolledW.h>
#include <Xm/TextF.h>
#include <Xm/ScrollBar.h>
#include <X11/xpm.h>
#include <X11/keysym.h>

/* For motif 1.2 */

#ifndef XmUNSET
#define XmUNSET False
#endif
#ifndef XmSET
#define XmSET True
#endif

/* Typewriter sound */

#ifndef HAS_PA_SOUND
#undef TYPEWRITER_SOUND
#endif

#ifdef TYPEWRITER_SOUND
#include <pulse/simple.h>
#include <pulse/error.h>
#include "typewriter_sound.h"

static int typewriter_sound_typers_count=0;
static int typewriter_sound_spaces_count=0;

static pa_simple *pa_s=NULL;
static pa_sample_spec pa_ss;
static int pa_error;

#endif

#define ABS(x) ( ((x)<0) ? (-(x)) : (x) )

#include "GIER.h"
#include "common.h"

#include "kb1.xpm"
#include "kb1.h"
#include "kb2.xpm"
#include "kb2.h"
#include "nimbi.xpm"
#include "nimbi.h"
#include "typewriter.h"

extern time_t start_time;

extern FILE *demofile;
/*
   Demo mode:

  0:	off
  1:	parsing commands
  2:	read next command at demosleep
  3:	wait for keyboard output to be demochar
  4:	wait for keyboard in input mode
  5:	wait for stop (ZQ)
*/
extern int demomode;
extern char demoline[100];
extern int demosleep;
extern int demochar;
static Widget demo_wnd=NULL;
static XtAppContext app;
extern unsigned long long clock_count, last_clock_count, base_clock_count, last_start_clock_count;
static unsigned long long nimbi_clock_count=0;


static Widget toplevel;
static Widget kb1_wnd=NULL, kb2_wnd=NULL, typewriter_wnd=NULL, reader_wnd=NULL, punch_wnd=NULL, printer_wnd=NULL, plotter_wnd=NULL, plotout_wnd=NULL, nimbi_wnd=NULL;
static int kb1_being_destroyed=0, kb2_being_destroyed=0, nimbi_being_destroyed=0;
static Widget bdisk_select[MAXBDISKS];
extern char *last_filename;

int n;
Arg wargs[1000];

/*
	External functions:
*/

extern void GIER_init(int, int, int *, int *, int *);
extern void GIER_save(char *);
extern void print_statistics();
extern void pa_sound_close();
extern int GIER_load(char *);
extern int pa_sound_init();
extern void GIER_single_step();
extern unsigned char flx2a(char, int *);
extern unsigned char a2flx(char, int *);
extern unsigned char remove_parity(unsigned char);
extern unsigned char remove_parity(unsigned char);
extern int ps_open(char *, int, double, int, int, int);
extern int ps_SY(unsigned char);
extern void ps_close();
extern int is_drum_running();
extern unsigned char set_parity(unsigned char);
extern void init_microcode();
extern void GIERmain();

/*
	Local variables:
*/

/*Main*/
Widget main_wnd;
Widget main_viewmenu, main_filedialog, main_optionsmenu, options_testmenu, options_aarhusmenu, main_debugmenu, main_newdialog_wnd, main_buffer1;
static int OKPressed, CancelPressed, Wnd_destroyed, debug_active=0;
static int filetype; /* 0: read, 1: write */
static int newdrum;
static int newBDisk_present[MAXBDISKS], newBDisk_tracksize[MAXBDISKS], newBDisk_size[MAXBDISKS];
/*kb1*/
static GC kb1_gc[8];
static Widget kb1_form, kb1_drawing;
static int selected_register;
static Coord lowerzero[42], lowerone[42], select_register[16];
static int kb12_upper_count[44], kb12_lower_count[44];
static int kb12_aux_count[10];
static int nimbi_count[14],last_nimbi_count[14];
static int kb12_count=0;
/*kb2*/
static GC kb2_gc[8];
static Widget kb2_form, kb2_drawing;
static Widget select_out[5][5];
/*typewriter*/
static Widget typewriter_form, typewriter_swindow, typewriter_horScroll,
       typewriter_verScroll, typewriter_input_label, typewriter_drawing;
static int typewriter_drawing_width, typewriter_drawing_height;
static short paper[LINEBUFFER][PAPERWIDTH][MAXOVERWRITE];
static int cur_row, cur_col;
static int uppercase; /* 0: lower, 1: upper */
static int old_uppercase;
static int redtext; /* 0: black, 1: red */
static int typewriter_font_width, typewriter_font_height;
static int in_copy=0;
static GC black_normal_gc;
static GC red_normal_gc;
static GC black_symbol_gc;
static GC red_symbol_gc;
static GC black_subscript_gc;
static GC red_subscript_gc;
static GC gc_table[2][3]; /* [redtext][0: normal, 1: symbol, 2: subscript] */
static GC scroll_gc;
static int typewriter_waiting=0;
static XComposeStatus compose;
static char strbuf[100];
#define RED_MASK 256
#define SYMBOL_MASK 512
#define typewriter_CursorX(col) ( (col)*typewriter_font_width )
#define typewriter_CursorY(row) ( (row)*typewriter_font_height + typewriter_applicationdata.textfont->ascent )
/*reader*/
static Widget reader_form, reader_reset, reader_pread, reader_skip, reader_up,
              reader_drawing, reader_filedialog;
static int reader_drawing_width, reader_drawing_height;
static int reader_current_fd = -1;
static int reader_modified = False;
static int reader_filetype; /* 0: flx, 1: asc */
static int reader_uppercase=0;
static int reader_nsavedchars=0;
static int reader_linepos=0;
static unsigned char reader_savedchars[10];
static int reader_font_width, reader_font_height;
#define reader_CursorX(col) ( (col)*reader_font_width )
#define reader_CursorY(row) ( (row)*reader_font_height + reader_applicationdata.textfont->ascent)
static GC reader_hole_gc, reader_gray_gc;
/*printer*/
static Widget printer_form, printer_newfile, printer_endfile, printer_psfilename;
static Widget printer_filedialog, printer_fileform, printer_fileselection;
static Widget papersize, lpi_edit, fontsize_edit, lines_edit, uadvance_select, uadvance_check;
static char lpi_buffer[20];
static char fontsize_buffer[20];
static char lines_buffer[20];
static int is_printing=0;
static int letter=0, lpi=6, lines=67;
Boolean uadvance=True;
static double fontsize=11.0;
/*plotter*/
static Widget plotter_form, plotter_newfile, plotter_endfile, plotout_psfilename;
static Widget plotout_filedialog, plotout_fileform, plotout_fileselection;
static Widget plotter_plot, plotter_cancelplot, plotter_buffersize;
static Widget plotter_center_select, plotter_autoup_select, plotter_autodown_select;
static Widget plotter_linecolor_pane, plotter_linecolor_select, plotter_linewidth_edit, plotter_linecolor_w[4];
static int plotter_letter=0;
static int plotter_characters=0;
static int plotter_center=1;
static int plotter_autoup=0;
static int plotter_autodown=0;
static int plotter_rotate=2; /* 0: no, 1: yes, 2: auto */
static int plotter_linewidth=4; /* 0.4 mm */
static char plotter_linewidth_buffer[4];
static int plotter_linecolor=0; /* 0: black, 1: red, 2: green, 3: blue */
static int plotter_pendown;
static int plotter_x, plotter_y;
static int plotter_xmin=99999999, plotter_xmax=-99999999, plotter_ymin=99999999, plotter_ymax=-99999999;
#define PLOTTERLINE_SIZE 1024
typedef struct _PLOTTERLINE
{
  struct _PLOTTERLINE *nextline;
  char buffer[PLOTTERLINE_SIZE];
  int buffersize;
  int linewidth; /* in 0.1 mm */
  int linecolor; /* 0: black, 1: red, 2: green, 3: blue */
  int x,y; /* coord of first point, in plotter steps */
} PLOTTERLINE;
static PLOTTERLINE *plotter_firstline=NULL;
static PLOTTERLINE *plotter_currentline=NULL;
static char plotter_code[16]={'0','1','2','3','4','5','6','7',
                              '8','9','a','b','c','d','e','f'};
static int plotter_dx[16]={  0,  0, -1, -1,  1,  1,  0,  0,
                             0,  0, -1,  0,  1,  0,  0,  0};
static int plotter_dy[16]={  0, -1,  0, -1,  0, -1,  0,  0,
                             1,  0,  1,  0,  1,  0,  0,  0};
typedef struct _PLOTTERMATRIX
{
  double v[2];
  double a[2][2];
} PLOTTERMATRIX;

/*punch*/
static Widget punch_form, punch_newtape, punch_endtape, punch_tapename, punch_filedialog=NULL, punch_drawing;
static int punch_drawing_width, punch_drawing_height;
static int punch_current_fd = -1;
static int punch_filetype; /* 0: flx, 1: asc */
static int punch_uppercase=0;
static int punch_font_width, punch_font_height;
#define punch_CursorX(col) ( (col)*punch_font_width )
#define punch_CursorY(row) ( (row)*punch_font_height + punch_applicationdata.textfont->ascent)
static GC punch_hole_gc, punch_gray_gc;
static char punch_char_table[PUNCH_TAPE_WINDOW];

/*Nimbi*/
static GC nimbi_gc[8];
static Widget nimbi_form, nimbi_drawing;

#undef FAKE_NIMBI

/*
	Forward declarations:
*/
int confirm(Widget,char *);
void GIERdemocommand();
void GIER_run();
void GIER_start();
void interface_update_all();
void kb1_update();
void kb1_init();
void kb1_destroy();
static void kb12_reset();
void kb12_update();
static void kb1_getstate();
static void kb2_getstate();
int kb1_is_visible();
void kb2_update();
void kb2_selected_update();
void kb2_init();
void kb2_destroy();
int kb2_is_visible();
static void typewriter_copywait();
void typewriter_SY(char);
void typewriter_wait(int);
void typewriter_lowercase();
void typewriter_init();
void typewriter_destroy();
int typewriter_is_visible();
void reader_update();
void reader_wait(int status);
void reader_init();
void reader_destroy();
int reader_is_visible();
void printer_init();
static void printer_fileopen_callback(Widget, XtPointer, XtPointer);
static void printer_fileclose_callback(Widget, XtPointer, XtPointer);
void printer_SY(char);
void printer_destroy();
int printer_is_visible();
void plotter_init();
static void plotter_newpath();
static void plotter_close();
static void plotout_fileopen_callback(Widget, XtPointer, XtPointer);
static void plotout_fileclose_callback(Widget, XtPointer, XtPointer);
void plotter_SY(char);
void plotter_destroy();
int plotter_is_visible();
void punch_SY(char);
void punch_init();
void punch_destroy();
int punch_is_visible();
void nimbi_update();
void nimbi_init();
void nimbi_destroy();
int nimbi_is_visible();

/* Work in progress */
#define MAXPROGRESSTITLE 80
static char progress_title[MAXPROGRESSTITLE];
static Widget progresswnd;
static int progress_maxvalue, progress_lastvalue;

static int nimbi_fd=-1;

static int nimbi_status=0;
/*
	nimbi_status:

  0:	Start, fd closed
  1:    fd open, no * received
  2:    fd open, * received during last two seconds

  When a * is received, last_nimbi_poll is set to 0.

  If no * is received in two seconds, close fd and set last_nimbi_poll to 0.

  If fd is closed (nimbi_status == 0), test if we can open the device and
  set last_nimbi_poll to 0 and nimbi_status to 1.

*/

static int last_nimbi_poll = 0;
static long long last_nimbi_change = 0;


typedef struct _BUTTONS
{
  char c;
  int type;
  GIERword mask40;
  unsigned short mask10;
} BUTTONS;

static GIERword oldMQ=0;
static unsigned short oldIN=1023;
static GIERword oldMQ2=0;
static unsigned short oldIN2=1023;

/*
 	Nimbi layout:


Nyt spil: IN0		IN1 Svar

              M28   M29

           M39   M30   M35

        M33   M34   M38   M36

           M32   M31   M37

*/

static BUTTONS buttons[14]=
{
  {'a', 0, ONE32, 0},
  {'b', 0, ONE31, 0},
  {'c', 0, ONE37, 0},
  {'d', 0, ONE33, 0},
  {'e', 0, ONE34, 0},
  {'f', 0, ONE38, 0},
  {'g', 0, ONE36, 0},
  {'h', 0, ONE39, 0},
  {'i', 0, ONE30, 0},
  {'j', 0, ONE35, 0},
  {'k', 1, 0, 512},
  {'l', 0, ONE28, 0},
  {'m', 0, ONE29, 0},
  {'n', 1, 0, 256}
};

static long long last_nimbi_clock_count=0;
static int nimbi_char_counter=0;

static int nbits(unsigned int n)
{
  n = (0x55555555u & n) + (0x55555555u & (n>> 1));
  n = (0x33333333u & n) + (0x33333333u & (n>> 2));
  n = (0x0f0f0f0fu & n) + (0x0f0f0f0fu & (n>> 4));
  n = (0x00ff00ffu & n) + (0x00ff00ffu & (n>> 8));
  n = (0x0000ffffu & n) + (0x0000ffffu & (n>>16));
  return n;
}

void nimbi_board_poll()
{
  int st;
  char c;
  int b;
  int full_sync;
  volatile int delayloop;

  /*
   	Output:
  	a-n:	Turn on
	A-N:	Turn off
	Input:
	a-n:	Release
	A-N:	Press
  */

  full_sync=0;
  return;

  for(delayloop=0; delayloop<0; delayloop++);
  last_nimbi_poll++;

  if(nimbi_status == 0)
  {
    if(last_nimbi_poll > gier_clock)
    {
      /* fprintf(stderr, "Test nimbi open.\n"); */
      last_nimbi_poll = 0;
#ifdef FAKE_NIMBI
      nimbi_fd = 1;
#else
      nimbi_fd = open("/dev/ttyACM0", O_RDWR|O_NONBLOCK);
#endif
      if(nimbi_fd >= 0)
      {
	nimbi_status = 1;
	full_sync=1;
	/* fprintf(stderr, "Nimbi status 1\n"); */
      }
    }
  }

  if(nimbi_status > 0)
  {
#ifndef FAKE_NIMBI
    while(read(nimbi_fd, &c, 1)==1)
    {
      /* fprintf(stderr, "Nimbi board: Got %c\n", c); */
      if(c>='a' && c<='n')
      {
	/* fprintf(stderr, "Nimbi board: Got %c\n", c); */
	b = c-'a';
	if(buttons[b].type == 0)
	{
	  /* Bit in MQ. Turn bit off when button released */
	  MQ &= ~(buttons[b].mask40);
	}
	else
	{
	  /* Bit in IN. Turn bit on when button released */
	  IN |= buttons[b].mask10;
	}
      }
      else if(c=='*')
      {
	last_nimbi_poll = 0;
	full_sync=1;
	nimbi_status = 2;
	/* fprintf(stderr, "Nimbi status 2\n"); */
      }
    }
#endif
    /* if(full_sync) fprintf(stderr, "full_sync\n"); */
    last_nimbi_change++;
    if( ((((oldMQ^MQ)&ONE28_39) != 0 ||
      ((oldIN^IN)&768) != 0 ) &&
      last_nimbi_change > 1000) || full_sync)
    {
      for(b=0; b<14; b++)
      {
        if(buttons[b].type == 0)
        {
	  if(((MQ^oldMQ)&buttons[b].mask40)||full_sync)
	  {
	    c = buttons[b].c - (MQ&buttons[b].mask40?32:0);
	    do
	    {
	      st = write(nimbi_fd, &c, 1);
	    } while(st==0);
	    /* if(!full_sync) fprintf(stderr, "Nimbi MQ send: %c\n", c); */
	  }
        }
        else
        {
	  if(((IN^oldIN)&buttons[b].mask10)||full_sync)
	  {
	    c = buttons[b].c - (IN&buttons[b].mask10?0:32);
	    do
	    {
	      st = write(nimbi_fd, &c, 1);
	    } while(st==0);
	    /* if(!full_sync) fprintf(stderr, "Nimbi IN send: %c\n", c); */
	  }
        }
      }
      oldMQ = MQ;
      oldIN = IN;
      last_nimbi_change = 0;
    }
#ifndef FAKE_NIMBI
    if(last_nimbi_poll > 450000)
    {
      close(nimbi_fd);
      nimbi_status = 0;
      last_nimbi_poll = 0;
      /* fprintf(stderr, "Nimbi status 0\n"); */
    }
#endif
  }
}


static void new_drum_callback(Widget w, XtPointer call_data, XtPointer client_data)
{
  newdrum = (long) call_data;
  if(debug&DEBUGinterface) fprintf(debug_fh, "newdrum: %d\n", newdrum);
}

static void bdisk_select_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  int bdisk, type, i;

  i = (long) client_data;
  bdisk = i/MAXBDISKTYPES;
  type = i%MAXBDISKTYPES;

  newBDisk_present[bdisk] = type>0;
  if(newBDisk_present[bdisk])
  {
    newBDisk_tracksize[bdisk]=bdisktypes[type].tracksize;
    newBDisk_size[bdisk]=bdisktypes[type].size;
    if(debug&DEBUGinterface) fprintf(debug_fh, "BDisk%d: tracksize: %d size: %d\n", bdisk+8,
	                     newBDisk_tracksize[bdisk], newBDisk_size[bdisk]);
  }
  else
  {
    newBDisk_tracksize[bdisk]=0;
    newBDisk_size[bdisk]=0;
    if(debug&DEBUGinterface) fprintf(debug_fh, "BDisk%d disabled.\n", bdisk+8);
  }
}

static void buffer_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  XmToggleButtonCallbackStruct *tgb = (XmToggleButtonCallbackStruct *) call_data;
  int value,i;

  value=True;
#if XmVERSION==1
  if(!tgb->set) value=False;
#else
  if(tgb->set == XmUNSET) value=False;
#endif
  if(debug&DEBUGinterface) fprintf(debug_fh, "buffer_callback called. value:%s\n", value?"True":"False");
  for(i=0; i<MAXBDISKS; i++)
  {
    XtSetSensitive(bdisk_select[i], value);
  }
}


static void newok_callback(Widget w, XtPointer call_data, XtPointer client_data)
{
  Boolean newbuffer;

  n=0;
  XtSetArg(wargs[n], XmNset, &newbuffer); n++;
  XtGetValues(main_buffer1, wargs, n);
  if(debug&DEBUGinterface) fprintf(debug_fh, "newbuffer: %s(%d)\n", newbuffer?"Yes":"No", newbuffer);

  XtUnrealizeWidget(main_newdialog_wnd);
  if(confirm(toplevel, "OK to erase everything and load empty core and backing store?"))
  {
    GIER_init(newdrum,newbuffer,newBDisk_tracksize,newBDisk_present,newBDisk_size);
  }
} 

static void newcancel_callback(Widget w, XtPointer call_data, XtPointer client_data)
{
  XtUnrealizeWidget(main_newdialog_wnd);
}

static void ok_callback(Widget w, XtPointer call_data, XtPointer client_data)
{
  OKPressed = TRUE;
} 

static void cancel_callback(Widget w, XtPointer call_data, XtPointer client_data)
{
  CancelPressed = TRUE;
}

static void destroy_callback(Widget w, XtPointer call_data, XtPointer client_data)
{
  CancelPressed = TRUE;
  Wnd_destroyed = TRUE;
}

int confirm(Widget refwnd,char *txt)
{
  Widget confirm_shell, form;
  Widget stext, ok_pb, cancel_pb;
  int x, y;
  XmString stmp;

  OKPressed = FALSE;
  CancelPressed = FALSE;
  Wnd_destroyed = FALSE;

  /* Create a popup shell for the confirm window */
  n = 0;
  XtSetArg(wargs[n], XtNtitle, "Confirm"); n++;
  XtSetArg(wargs[n], XmNmessageString, (stmp=XmStringCreateLocalized(txt))); n++;
  XtSetArg(wargs[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++;
  XtSetArg(wargs[n], XmNautoUnmanage, FALSE); n++;
  XtSetArg(wargs[n], XmNnoResize, TRUE); n++;
  XtSetArg(wargs[n], XmNresizePolicy, XmRESIZE_NONE); n++;
  XtSetArg(wargs[n], XmNdeleteResponse, XmDESTROY); n++;
  confirm_shell = XmCreateWarningDialog(refwnd, "confirm", wargs, n);
  XmStringFree(stmp);

  XtUnmanageChild(XmMessageBoxGetChild(confirm_shell, XmDIALOG_HELP_BUTTON));
  				
  XtAddCallback(confirm_shell, XmNdestroyCallback, destroy_callback, NULL);
  XtAddCallback(confirm_shell, XmNokCallback, ok_callback, NULL);
  XtAddCallback(confirm_shell, XmNcancelCallback, cancel_callback, NULL);

  XtManageChild(confirm_shell);

  /* Local event loop - wait for ok or cancel */
  while (!OKPressed && !CancelPressed)
  {
    XtAppProcessEvent(app,XtIMAll);
  }

  /* Popdown and destroy the confirm window */
  if (!Wnd_destroyed)
  {
    XtUnmanageChild(confirm_shell);
    XtDestroyWidget(confirm_shell);
  }

  return OKPressed;
}

void message(Widget refwnd,char *txt)
{
  Widget message_shell, form;
  Widget stext, ok_cb;
  int x, y;
  XmString stmp;

  OKPressed = FALSE;
  Wnd_destroyed = FALSE;

  /* Create a popup shell for the message window */
  n = 0;
  XtSetArg(wargs[n], XtNtitle, "Message"); n++;
  XtSetArg(wargs[n], XmNmessageString, (stmp=XmStringCreateLocalized(txt))); n++;
  XtSetArg(wargs[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++;
  XtSetArg(wargs[n], XmNautoUnmanage, FALSE); n++;
  XtSetArg(wargs[n], XmNnoResize, TRUE); n++;
  XtSetArg(wargs[n], XmNresizePolicy, XmRESIZE_NONE); n++;
  message_shell = XmCreateWarningDialog(refwnd, "message", wargs, n);
  XmStringFree(stmp);

  XtUnmanageChild(XmMessageBoxGetChild(message_shell, XmDIALOG_CANCEL_BUTTON));
  XtUnmanageChild(XmMessageBoxGetChild(message_shell, XmDIALOG_HELP_BUTTON));
  				
  XtAddCallback(message_shell, XmNdestroyCallback, destroy_callback, NULL);
  XtAddCallback(message_shell, XmNokCallback, ok_callback, NULL);

  XtManageChild(message_shell);

  /* Local event loop - wait for ok or cancel */
  while (!OKPressed && !Wnd_destroyed)
  {
    XtAppProcessEvent(app,XtIMAll);
  };

  /* Popdown and destroy the message window */
  if (!Wnd_destroyed)
  {
    XtUnmanageChild(message_shell);
    XtDestroyWidget(message_shell);
  }
}

void progress_setvalue(int value)
{
  char txt[MAXPROGRESSTITLE+20];
  XmString stmp;
  int thisvalue;

  if(Wnd_destroyed)return;

  thisvalue = (int)(((double)value)*100.0/((double)progress_maxvalue));
  if(thisvalue>100) thisvalue=100;

  if(thisvalue>=progress_lastvalue+5)
  {
    sprintf(txt, "%s: %3d%% done", progress_title, thisvalue);
    n=0;
    XtSetArg(wargs[n], XmNmessageString, (stmp=XmStringCreateLocalized(txt))); n++;
    XtSetValues(progresswnd, wargs, n);
    XmStringFree(stmp);
    while(XtAppPending(app))
    {
      XtAppProcessEvent(app,XtIMAll);
    }
    progress_lastvalue=thisvalue;
  }
}

void progress_stop()
{
  if(!Wnd_destroyed)
  {
    XtUnmanageChild(progresswnd);
    XtDestroyWidget(progresswnd);
  }
}

void progress_init(int maxvalue, char *title)
{
  progress_maxvalue = maxvalue;
  strncpy(progress_title, title, MAXPROGRESSTITLE);
  progress_title[MAXPROGRESSTITLE-1] = '\0';
  progress_lastvalue = -1000000;

  Wnd_destroyed = FALSE;
  
  /* Create a popup shell for the progress window */
  n = 0;
  XtSetArg(wargs[n], XtNtitle, "Progress"); n++;
  XtSetArg(wargs[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++;
  XtSetArg(wargs[n], XmNautoUnmanage, FALSE); n++;
  XtSetArg(wargs[n], XmNnoResize, TRUE); n++;
  XtSetArg(wargs[n], XmNresizePolicy, XmRESIZE_NONE); n++;
  XtSetArg(wargs[n], XmNdeleteResponse, XmDESTROY); n++;
  progresswnd = XmCreateWorkingDialog(toplevel, "Progress", wargs, n);
  XtAddCallback(progresswnd, XmNdestroyCallback, destroy_callback, NULL);

  XtUnmanageChild(XmMessageBoxGetChild(progresswnd, XmDIALOG_HELP_BUTTON));
  XtUnmanageChild(XmMessageBoxGetChild(progresswnd, XmDIALOG_OK_BUTTON));
  XtUnmanageChild(XmMessageBoxGetChild(progresswnd, XmDIALOG_CANCEL_BUTTON));

  progress_setvalue(0);
  				
  XtManageChild(progresswnd);
}

void GIER_exit()
{
  if(is_printing) ps_close();
  GIER_save("default.gier");
  print_statistics();
  /* dumpgier(); */
#ifdef HAS_OSS_SOUND
  if(sound_enabled) oss_sound_close();
#endif
#ifdef HAS_PA_SOUND
  if(sound_enabled) pa_sound_close();
#endif
  exit(0);
}

static void maindestroy_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  GIER_exit();
}

static void main_fileopen_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  XmFileSelectionBoxCallbackStruct *cbs = (XmFileSelectionBoxCallbackStruct *)call_data;
  char *filename;

  XmStringGetLtoR(cbs->value, XmSTRING_DEFAULT_CHARSET, &filename);
  XtUnmanageChild(w);

  if(filetype == 0)
  {
    if(confirm(toplevel, "OK to erase everything and load new core and backing store?"))
    {
      if(GIER_load(filename)==0)
      {
        message(toplevel, "Reading the file failed.\nDefault setup has been loaded.");
	GIER_init(30,True,NULL,NULL,NULL);
      }
      else
      {
	if(last_filename != NULL) free(last_filename);
	last_filename = malloc(strlen(filename)+1);
	strcpy(last_filename, filename);
      }
      if(running) GIER_start();
      interface_update_all();
    }
  }
  else
  {
    if(last_filename != NULL) free(last_filename);
    last_filename = malloc(strlen(filename)+1);
    strcpy(last_filename, filename);

    GIER_save(filename);
  }

  XtFree(filename);
}

static void main_fileclose_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  XtUnmanageChild(w);
}

static void new_action()
{
  if(debug&DEBUGinterface) fprintf(debug_fh, "new_action called.\n");
  XtRealizeWidget(main_newdialog_wnd);
}

static void open_action()
{
  filetype=0;
  XmFileSelectionDoSearch(main_filedialog, NULL);
  XtManageChild(main_filedialog);
}

static void save_as_action()
{
  filetype=1;
  XmFileSelectionDoSearch(main_filedialog, NULL);
  XtManageChild(main_filedialog);
}

static void save_action()
{
  if(last_filename == NULL)
  {
    save_as_action();
  }
  else
  {
    GIER_save(last_filename);
  }
}

static void quit_action()
{
  if(confirm(toplevel, "OK to exit GIER simulator?"))
  {
    GIER_exit();
  }
}

static void filemenu_callback(Widget w, XtPointer call_data, XtPointer client_data)
{
  Cardinal item_no = (long) call_data;
  switch(item_no)
  {
  case 0: /* New */
    new_action();
    break;
  case 1: /* Open */
    open_action();
    break;
  case 2: /* Save */
    save_action();
    break;
  case 3: /* Save As */
    save_as_action();
    break;
  case 4: /* Quit */
    quit_action();
    break;
  }
}

void update_viewmenu()
{
  XtVaSetValues(XtNameToWidget(main_viewmenu, "button_0"), XmNset, kb1_wnd!=NULL, NULL);
  XtVaSetValues(XtNameToWidget(main_viewmenu, "button_1"), XmNset, kb2_wnd!=NULL, NULL);
  XtVaSetValues(XtNameToWidget(main_viewmenu, "button_2"), XmNset, typewriter_wnd!=NULL, NULL);
  XtVaSetValues(XtNameToWidget(main_viewmenu, "button_3"), XmNset, reader_wnd!=NULL, NULL);
  XtVaSetValues(XtNameToWidget(main_viewmenu, "button_4"), XmNset, punch_wnd!=NULL, NULL);
  XtVaSetValues(XtNameToWidget(main_viewmenu, "button_5"), XmNset, printer_wnd!=NULL, NULL);
  XtVaSetValues(XtNameToWidget(main_viewmenu, "button_6"), XmNset, plotter_wnd!=NULL, NULL);
  XtVaSetValues(XtNameToWidget(main_viewmenu, "button_7"), XmNset, nimbi_wnd!=NULL, NULL);
}

static void viewmenu_callback(Widget w, XtPointer call_data, XtPointer client_data)
{
  Cardinal item_no = (long) call_data;
  XmToggleButtonCallbackStruct *cbs = (XmToggleButtonCallbackStruct *) client_data;

  switch(item_no)
  {
  case 0: /* Main control board */
    if(cbs->set)
    {
      kb1_init();
    }
    else
    {
      kb1_destroy();
    }
    break;
  case 1: /* Aux. control board */
    if(cbs->set)
    {
      kb2_init();
    }
    else
    {
      kb2_destroy();
    }
    break;
  case 2: /* Typewriter */
    if(cbs->set)
    {
      typewriter_init();
    }
    else
    {
      typewriter_destroy();
    }
    break;
  case 3: /* Tape reader */
    if(cbs->set)
    {
      reader_init();
    }
    else
    {
      reader_destroy();
    }
    break;
  case 4: /* Tape punch */
    if(cbs->set)
    {
      punch_init();
    }
    else
    {
      punch_destroy();
    }
    break;
  case 5: /* Line printer */
    if(cbs->set)
    {
      printer_init();
    }
    else
    {
      printer_destroy();
    }
    break;
  case 6: /* Plotter */
    if(cbs->set)
    {
      plotter_init();
    }
    else
    {
      plotter_destroy();
    }
    break;
  case 7: /* Nimbi */
    if(cbs->set)
    {
      nimbi_init();
    }
    else
    {
      nimbi_destroy();
    }
    break;
  }
}

void update_optionsmenu()
{
  XtVaSetValues(XtNameToWidget(main_optionsmenu, "button_0"), XmNset, !track0_open, NULL);
  XtVaSetValues(XtNameToWidget(main_optionsmenu, "button_1"), XmNset, !track1_31_open, NULL);
  XtVaSetValues(XtNameToWidget(main_optionsmenu, "button_2"), XmNset, switch_k0, NULL);
  XtVaSetValues(XtNameToWidget(main_optionsmenu, "button_3"), XmNset, sound_enabled, NULL);
}

void update_aarhusmenu()
{
  XtVaSetValues(XtNameToWidget(options_aarhusmenu, "button_0"), XmNset, (aarhus_mode==0), NULL);
  XtVaSetValues(XtNameToWidget(options_aarhusmenu, "button_1"), XmNset, (aarhus_mode==1), NULL);
  XtVaSetValues(XtNameToWidget(options_aarhusmenu, "button_2"), XmNset, (aarhus_mode==2), NULL);
  XtVaSetValues(XtNameToWidget(options_aarhusmenu, "button_3"), XmNset, (aarhus_mode==3), NULL);
  XtVaSetValues(XtNameToWidget(options_aarhusmenu, "button_4"), XmNset, (aarhus_mode==4), NULL);
}

void update_testmenu()
{
  XtVaSetValues(XtNameToWidget(options_testmenu, "button_0"), XmNset, (testmode==0), NULL);
  XtVaSetValues(XtNameToWidget(options_testmenu, "button_1"), XmNset, (testmode==1), NULL);
  XtVaSetValues(XtNameToWidget(options_testmenu, "button_2"), XmNset, (testmode==2), NULL);
  XtVaSetValues(XtNameToWidget(options_testmenu, "button_3"), XmNset, (testmode==3), NULL);
  XtVaSetValues(XtNameToWidget(options_testmenu, "button_4"), XmNset, (testmode==4), NULL);
  XtVaSetValues(XtNameToWidget(options_testmenu, "button_5"), XmNset, (testmode==5), NULL);
}

static void aarhusmenu_callback(Widget w, XtPointer call_data, XtPointer client_data)
{
  Cardinal item_no = (long) call_data;
  aarhus_mode=item_no;
  if(debug&DEBUGinterface) fprintf(debug_fh, "aarhus_mode: %d\n",aarhus_mode);
  update_aarhusmenu();
}

static void testmenu_callback(Widget w, XtPointer call_data, XtPointer client_data)
{
  Cardinal item_no = (long) call_data;
  testmode=item_no;
  if(debug&DEBUGinterface) fprintf(debug_fh, "testmode: %d\n",testmode);
  update_testmenu();
}

static void optionsmenu_callback(Widget w, XtPointer call_data, XtPointer client_data)
{
  Cardinal item_no = (long) call_data;
  XmToggleButtonCallbackStruct *cbs = (XmToggleButtonCallbackStruct *) client_data;
  if(debug&DEBUGinterface) fprintf(debug_fh, "optionsmenu_callback item_no: %d\n",item_no);
  switch(item_no)
  {
  case 0:
    track0_open = !cbs->set;
    break;
  case 1:
    track1_31_open = !cbs->set;
    break;
  case 2:
    switch_k0 = cbs->set;
    break;
  case 3:
    sound_enabled = cbs->set;
    break;
  }
}

static void InitDebug()
{
  if(!debug_active)
  {
    debug_fh = stderr;
    debug_fh = fopen("gier.debug", "w");
    debug_active = True;
  }
}

static void EndDebug()
{
  if(debug_active)
  {
    fclose(debug_fh);
    debug_active = False;
  }
}

void checkdebug()
{
  if(debug&(~DEBUGstat))
  {
    InitDebug();
  }
  else
  {
    EndDebug();
  }
}

void update_debugmenu()
{
  checkdebug();
  XtVaSetValues(XtNameToWidget(main_debugmenu, "button_1"), XmNset, (debug&DEBUGmemory)!=0, NULL);
  XtVaSetValues(XtNameToWidget(main_debugmenu, "button_2"), XmNset, (debug&DEBUGmicrostep)!=0, NULL);
  XtVaSetValues(XtNameToWidget(main_debugmenu, "button_3"), XmNset, (debug&DEBUGmaskintal)!=0, NULL);
  XtVaSetValues(XtNameToWidget(main_debugmenu, "button_4"), XmNset, (debug&DEBUGflydende)!=0, NULL);
  XtVaSetValues(XtNameToWidget(main_debugmenu, "button_5"), XmNset, (debug&DEBUGBUS)!=0, NULL);
  XtVaSetValues(XtNameToWidget(main_debugmenu, "button_6"), XmNset, (debug&DEBUGregisters)!=0, NULL);
  XtVaSetValues(XtNameToWidget(main_debugmenu, "button_7"), XmNset, (debug&DEBUGindicator)!=0, NULL);
  XtVaSetValues(XtNameToWidget(main_debugmenu, "button_8"), XmNset, (debug&DEBUGinterface)!=0, NULL);
  XtVaSetValues(XtNameToWidget(main_debugmenu, "button_9"), XmNset, (debug&DEBUGdrum)!=0, NULL);
  XtVaSetValues(XtNameToWidget(main_debugmenu, "button_10"), XmNset, (debug&DEBUGsound)!=0, NULL);
  XtVaSetValues(XtNameToWidget(main_debugmenu, "button_11"), XmNset, (debug&DEBUGbuffer)!=0, NULL);
  XtVaSetValues(XtNameToWidget(main_debugmenu, "button_12"), XmNset, (debug&DEBUGexecute)!=0, NULL);
  XtVaSetValues(XtNameToWidget(main_debugmenu, "button_13"), XmNset, (debug&DEBUGtext)!=0, NULL);
  XtVaSetValues(XtNameToWidget(main_debugmenu, "button_14"), XmNset, (debug&DEBUGstat)!=0, NULL);
  XtVaSetValues(XtNameToWidget(main_debugmenu, "button_15"), XmNset, (debug&DEBUGdrumrace)!=0, NULL);
}

static void debugmenu_callback(Widget w, XtPointer call_data, XtPointer client_data)
{
  Cardinal item_no = (long) call_data;
  XmToggleButtonCallbackStruct *cbs = (XmToggleButtonCallbackStruct *) client_data;
  switch(item_no)
  {
  case 0:
    debug = 0;
    update_debugmenu();
    break;
  case 1:
    debug &= ~DEBUGmemory;
    if(cbs->set) debug |= DEBUGmemory;
    break;
  case 2:
    debug &= ~DEBUGmicrostep;
    if(cbs->set) debug |= DEBUGmicrostep;
    break;
  case 3:
    debug &= ~DEBUGmaskintal;
    if(cbs->set) debug |= DEBUGmaskintal;
    break;
  case 4:
    debug &= ~DEBUGflydende;
    if(cbs->set) debug |= DEBUGflydende;
    break;
  case 5:
    debug &= ~DEBUGBUS;
    if(cbs->set) debug |= DEBUGBUS;
    break;
  case 6:
    debug &= ~DEBUGregisters;
    if(cbs->set) debug |= DEBUGregisters;
    break;
  case 7:
    debug &= ~DEBUGindicator;
    if(cbs->set) debug |= DEBUGindicator;
    break;
  case 8:
    debug &= ~DEBUGinterface;
    if(cbs->set) debug |= DEBUGinterface;
    break;
  case 9:
    debug &= ~DEBUGdrum;
    if(cbs->set) debug |= DEBUGdrum;
    break;
  case 10:
    debug &= ~DEBUGsound;
    if(cbs->set) debug |= DEBUGsound;
    break;
  case 11:
    debug &= ~DEBUGbuffer;
    if(cbs->set) debug |= DEBUGbuffer;
    break;
  case 12:
    debug &= ~DEBUGexecute;
    if(cbs->set) debug |= DEBUGexecute;
    break;
  case 13:
    debug &= ~DEBUGtext;
    if(cbs->set) debug |= DEBUGtext;
    break;
  case 14:
    print_statistics();
    debug &= ~DEBUGstat;
    if(cbs->set) debug |= DEBUGstat;
    break;
  case 15:
    debug &= ~DEBUGdrumrace;
    if(cbs->set) debug |= DEBUGdrumrace;
    break;
  case 16:
    debug = ~0;
    update_debugmenu();
    break;
  }
  checkdebug();
}

void main_wnd_init()
{
  Atom atom;
  Widget menubar, filemenu;
  /*
  	This is the main window of the GIER simulator.

  */
  Widget new_form;
  Widget new_label1, new_radio1, drum1, drum2, drum3, 
	 disc1, disc2, disc3, disc4, disc5, disc6, separator1, 
         new_label2, new_check1, separator2, new_ok, new_cancel,
	 bdisk_label[MAXBDISKS], bdisk_pane[MAXBDISKS],
	 bdisk_w[MAXBDISKS][MAXBDISKTYPES];
  XmString s[20];
  char tmp_label[80];
  char tmp_title[80];
  int i, j;

  n=0;
  XtSetArg(wargs[n], XtNtitle, "GIER Simulator"); n++;
  XtSetArg(wargs[n], XtNgeometry, "+10+10"); n++;
  main_wnd = XmCreateMainWindow(toplevel, "main", wargs, n);
  atom = XmInternAtom(XtDisplay(toplevel),"WM_DELETE_WINDOW",TRUE);
  XmAddWMProtocolCallback(toplevel, atom, maindestroy_callback, False);
  XtManageChild(main_wnd);

  /* Menu bar */

  menubar = XmVaCreateSimpleMenuBar(main_wnd, "menubar",
              XmVaCASCADEBUTTON, s[0] = XmStringCreateSimple("File"), (KeySym) 'F',	/* File		*/
	      XmVaCASCADEBUTTON, s[1] = XmStringCreateSimple("View"), (KeySym) 'V',	/* View		*/
	      XmVaCASCADEBUTTON, s[2] = XmStringCreateSimple("Options"), (KeySym) 'O',	/* Options	*/
	      XmVaCASCADEBUTTON, s[3] = XmStringCreateSimple("Debug"), (KeySym) 'D',	/* Debug	*/
	      NULL);
  for(i=0; i<4; i++) XmStringFree(s[i]);

  filemenu = XmVaCreateSimplePulldownMenu(menubar, "filemenu", 0, filemenu_callback,
              XmVaPUSHBUTTON, s[0] = XmStringCreateSimple("New"), (KeySym) 'N', NULL, NULL,	/* New		*/
	      XmVaPUSHBUTTON, s[1] = XmStringCreateSimple("Open"), (KeySym) 'O', NULL, NULL,	/* Open		*/
	      XmVaSEPARATOR,
	      XmVaPUSHBUTTON, s[2] = XmStringCreateSimple("Save"), (KeySym) 'S', NULL, NULL,	/* Save		*/
	      XmVaPUSHBUTTON, s[3] = XmStringCreateSimple("Save As.."), (KeySym) 'A', NULL, NULL,	/* Save	As..	*/
	      XmVaSEPARATOR,
	      XmVaPUSHBUTTON, s[4] = XmStringCreateSimple("Quit"), (KeySym) 'Q', NULL, NULL,	/* Quit		*/
	      NULL);
  for(i=0; i<5; i++) XmStringFree(s[i]);

  main_viewmenu = XmVaCreateSimplePulldownMenu(menubar, "viewmenu", 1, viewmenu_callback,
              XmVaCHECKBUTTON, s[0] = XmStringCreateSimple("View Main Control Board"), (KeySym) 'M', NULL, NULL,	/* Show main control board */
              XmVaCHECKBUTTON, s[1] = XmStringCreateSimple("View Aux. Control Board"), (KeySym) 'A', NULL, NULL,	/* Show auxilliary control board */
              XmVaCHECKBUTTON, s[2] = XmStringCreateSimple("View Typewriter"), (KeySym) 'T', NULL, NULL,		/* Show typewriter */
              XmVaCHECKBUTTON, s[3] = XmStringCreateSimple("View Tape Reader"), (KeySym) 'R', NULL, NULL,	/* Show tape reader */
              XmVaCHECKBUTTON, s[4] = XmStringCreateSimple("View Tape Punch"), (KeySym) 'P', NULL, NULL,		/* Show punch */
              XmVaCHECKBUTTON, s[5] = XmStringCreateSimple("View Line Printer"), (KeySym) 'L', NULL, NULL,		/* Show lineprinter */
              XmVaCHECKBUTTON, s[6] = XmStringCreateSimple("View Plotter"), (KeySym) 'l', NULL, NULL,		/* Show plotter */
              XmVaCHECKBUTTON, s[7] = XmStringCreateSimple("View Nimbi"), (KeySym) 'N', NULL, NULL,		/* Show Nimbi */
	      NULL);
  for(i=0; i<8; i++) XmStringFree(s[i]);

  main_optionsmenu = XmVaCreateSimplePulldownMenu(menubar, "optionsmenu", 2, optionsmenu_callback,
              XmVaCHECKBUTTON, s[0] = XmStringCreateSimple("Channel 0 readonly"), (KeySym) '0', NULL, NULL,	/* Write protect channel 0 */
              XmVaCHECKBUTTON, s[1] = XmStringCreateSimple("Channel 1-31 readonly"), (KeySym) '1', NULL, NULL,	/* Write protect channel 1-31 */
              XmVaCHECKBUTTON, s[2] = XmStringCreateSimple("Floating Zero"), (KeySym) 'Z', NULL, NULL,		/* FP 0 is 0 */
              XmVaCHECKBUTTON, s[3] = XmStringCreateSimple("Use Sound"), (KeySym) 'S', NULL, NULL,		/* Use sound */
              XmVaCASCADEBUTTON, s[4] = XmStringCreateSimple("Aarhus"), (KeySym) 'A',                /* Aarhus menu */
              XmVaCASCADEBUTTON, s[5] = XmStringCreateSimple("Test"), (KeySym) 'T',			/* Test menu */
	      NULL);
  for(i=0; i<6; i++) XmStringFree(s[i]);
  update_optionsmenu();

  options_testmenu = XmVaCreateSimplePulldownMenu(main_optionsmenu, "testmenu", 5, testmenu_callback,
              XmVaRADIOBUTTON, s[0]= XmStringCreateSimple("Normal"), (KeySym) '0', NULL, NULL,
              XmVaRADIOBUTTON, s[1]= XmStringCreateSimple("1: Skriv i FL"), (KeySym) '1', NULL, NULL,
              XmVaRADIOBUTTON, s[2]= XmStringCreateSimple("2: Læs i FL"), (KeySym) '2', NULL, NULL,
              XmVaRADIOBUTTON, s[3]= XmStringCreateSimple("3: Højre skift"), (KeySym) '3', NULL, NULL,
              XmVaRADIOBUTTON, s[4]= XmStringCreateSimple("4: Venstre skift"), (KeySym) '4', NULL, NULL,
              XmVaRADIOBUTTON, s[5]= XmStringCreateSimple("5: Indikatortest"), (KeySym) '5', NULL, NULL,
	      NULL);
  for(i=0; i<6; i++) XmStringFree(s[i]);

  options_aarhusmenu = XmVaCreateSimplePulldownMenu(main_optionsmenu, "aarhusmenu", 4, aarhusmenu_callback,
              XmVaRADIOBUTTON, s[0]= XmStringCreateSimple("38"), (KeySym) '0', NULL, NULL,
              XmVaRADIOBUTTON, s[1]= XmStringCreateSimple("1"), (KeySym) '1', NULL, NULL,
              XmVaRADIOBUTTON, s[2]= XmStringCreateSimple("319"), (KeySym) '2', NULL, NULL,
              XmVaRADIOBUTTON, s[3]= XmStringCreateSimple("639"), (KeySym) '3', NULL, NULL,
              XmVaRADIOBUTTON, s[4]= XmStringCreateSimple("959"), (KeySym) '4', NULL, NULL,
	      NULL);
  for(i=0; i<5; i++) XmStringFree(s[i]);

  update_aarhusmenu();
  update_testmenu();

  main_debugmenu = XmVaCreateSimplePulldownMenu(menubar, "debugmenu", 3, debugmenu_callback,
	      XmVaPUSHBUTTON, s[0] = XmStringCreateSimple("None"), (KeySym) 'o', NULL, NULL,		/* None		*/
	      XmVaSEPARATOR,
              XmVaCHECKBUTTON, s[1] = XmStringCreateSimple("Core store"), (KeySym) 'C', NULL, NULL,	/* DEBUGmemory */
              XmVaCHECKBUTTON, s[2] = XmStringCreateSimple("Microstep"), (KeySym) 'M', NULL, NULL,	/* DEBUGmicrostep */
              XmVaCHECKBUTTON, s[3] = XmStringCreateSimple("Machine Number"), (KeySym) 'N', NULL, NULL,	/* DEBUGmaskintal */
              XmVaCHECKBUTTON, s[4] = XmStringCreateSimple("Floating number"), (KeySym) 'F', NULL, NULL,	/* DEBUGflydende */
              XmVaCHECKBUTTON, s[5] = XmStringCreateSimple("BUS"), (KeySym) 'B', NULL, NULL,		/* DEBUGBUS */
              XmVaCHECKBUTTON, s[6] = XmStringCreateSimple("Registers"), (KeySym) 'R', NULL, NULL,	/* DEBUGregisters */
              XmVaCHECKBUTTON, s[7] = XmStringCreateSimple("Indicator"), (KeySym) 'I', NULL, NULL,	/* DEBUGindicator */
              XmVaCHECKBUTTON, s[8] = XmStringCreateSimple("User interface"), (KeySym) 'U', NULL, NULL,	/* DEBUGinterface */
              XmVaCHECKBUTTON, s[9] = XmStringCreateSimple("Drum"), (KeySym) 'D', NULL, NULL,		/* DEBUGdrum */
              XmVaCHECKBUTTON, s[10] = XmStringCreateSimple("Sound"), (KeySym) 'S', NULL, NULL,		/* DEBUGsound */
              XmVaCHECKBUTTON, s[11] = XmStringCreateSimple("Buffer"), (KeySym) 'u', NULL, NULL,		/* DEBUGbuffer */
              XmVaCHECKBUTTON, s[12] = XmStringCreateSimple("Execute"), (KeySym) 'x', NULL, NULL,	/* DEBUGexecute */
              XmVaCHECKBUTTON, s[13] = XmStringCreateSimple("Text"), (KeySym) 'T', NULL, NULL,		/* DEBUGtext */
              XmVaCHECKBUTTON, s[14] = XmStringCreateSimple("Statistics"), (KeySym) 'S', NULL, NULL,	/* DEBUGstat */
              XmVaCHECKBUTTON, s[15] = XmStringCreateSimple("Drum race"), (KeySym) 'r', NULL, NULL,	/* DEBUGdrumrace */
	      XmVaSEPARATOR,
	      XmVaPUSHBUTTON, s[16] = XmStringCreateSimple("All"), (KeySym) 'A', NULL, NULL,		/* All		*/
	      NULL);
  for(i=0; i<17; i++) XmStringFree(s[i]);

  update_debugmenu();

  XtManageChild(menubar);

  /* Create file dialog for save/load */

  n=0;
  XtSetArg(wargs[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++;
  XtSetArg(wargs[n], XmNautoUnmanage, False); n++;
  XtSetArg(wargs[n], XmNnoResize, TRUE); n++;
  XtSetArg(wargs[n], XmNresizePolicy, XmRESIZE_NONE); n++;
  XtSetArg(wargs[n], XmNpattern, (s[0]=XmStringCreateSimple("*.gier"))); n++;
  XtSetArg(wargs[n], XmNdialogTitle, (s[1]=XmStringCreateSimple("GIER Setup File Selection"))); n++;

  main_filedialog = XmCreateFileSelectionDialog(main_wnd, "FileDialog", wargs, n);
  for(i=0; i<2; i++) XmStringFree(s[i]);

  XtAddCallback(main_filedialog, XmNokCallback, main_fileopen_callback, (XtPointer)NULL);
  XtAddCallback(main_filedialog, XmNcancelCallback, main_fileclose_callback, (XtPointer)NULL);

  /* Create dialog for initializing GIER.

     Currently, you can only choose between 1,2,3 drums or
     a disc, but hopefully it should be possible to enable
     buffer store and buffer devices.
  */

  n=0;
  XtSetArg(wargs[n], XtNtitle, "GIER setup"); n++;
  main_newdialog_wnd = XtAppCreateShell("gier_setup", "gier_setup", topLevelShellWidgetClass, XtDisplay(toplevel), wargs, n);

  n=0;
  new_form = XtCreateManagedWidget("form", xmFormWidgetClass, main_newdialog_wnd, wargs, n);

  n=0;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNlabelString, (s[0]=XmStringCreateSimple("Select Drum/Disc:"))); n++;
  new_label1 = XmCreateLabel(new_form, "DrumTitle", wargs, n);
  XtManageChild(new_label1);
  XmStringFree(s[0]);

  n=0;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, new_label1); n++;
  new_radio1 = XmCreateRadioBox(new_form, "RadioBox1", wargs, n);
  XtManageChild(new_radio1);

  newdrum=1;

  n = 0;
  XtSetArg(wargs[n], XmNlabelString, (s[0]=XmStringCreateSimple("1 Drum"))); n++;
  XtSetArg(wargs[n], XmNset, True); n++;
  drum1 = XmCreateToggleButton(new_radio1, "ToggleDrum1", wargs, n);
  XtManageChild(drum1);
  XmStringFree(s[0]);
  XtAddCallback(drum1, XmNarmCallback, new_drum_callback, (XtPointer) 1);

  n = 0;
  XtSetArg(wargs[n], XmNlabelString, (s[0]=XmStringCreateSimple("2 Drums"))); n++;
  drum2 = XmCreateToggleButton(new_radio1, "ToggleDrum2", wargs, n);
  XtManageChild(drum2);
  XmStringFree(s[0]);
  XtAddCallback(drum2, XmNarmCallback, new_drum_callback, (XtPointer) 2);

  n = 0;
  XtSetArg(wargs[n], XmNlabelString, (s[0]=XmStringCreateSimple("3 Drums"))); n++;
  drum3 = XmCreateToggleButton(new_radio1, "ToggleDrum3", wargs, n);
  XtManageChild(drum3);
  XmStringFree(s[0]);
  XtAddCallback(drum3, XmNarmCallback, new_drum_callback, (XtPointer) 3);

  n = 0;
  XtSetArg(wargs[n], XmNlabelString, (s[0]=XmStringCreateSimple("1 Disc"))); n++;
  disc1 = XmCreateToggleButton(new_radio1, "ToggleDisc1", wargs, n);
  XtManageChild(disc1);
  XmStringFree(s[0]);
  XtAddCallback(disc1, XmNarmCallback, new_drum_callback, (XtPointer) 30);

  n = 0;
  XtSetArg(wargs[n], XmNlabelString, (s[0]=XmStringCreateSimple("2 Discs"))); n++;
  disc2 = XmCreateToggleButton(new_radio1, "ToggleDisc2", wargs, n);
  XtManageChild(disc2);
  XmStringFree(s[0]);
  XtAddCallback(disc2, XmNarmCallback, new_drum_callback, (XtPointer) 60);

  n = 0;
  XtSetArg(wargs[n], XmNlabelString, (s[0]=XmStringCreateSimple("3 Discs"))); n++;
  disc3 = XmCreateToggleButton(new_radio1, "ToggleDisc3", wargs, n);
  XtManageChild(disc3);
  XmStringFree(s[0]);
  XtAddCallback(disc3, XmNarmCallback, new_drum_callback, (XtPointer) 90);

  n = 0;
  XtSetArg(wargs[n], XmNlabelString, (s[0]=XmStringCreateSimple("4 Discs"))); n++;
  disc4 = XmCreateToggleButton(new_radio1, "ToggleDisc4", wargs, n);
  XtManageChild(disc4);
  XmStringFree(s[0]);
  XtAddCallback(disc4, XmNarmCallback, new_drum_callback, (XtPointer) 120);

  n = 0;
  XtSetArg(wargs[n], XmNlabelString, (s[0]=XmStringCreateSimple("5 Discs"))); n++;
  disc5 = XmCreateToggleButton(new_radio1, "ToggleDisc5", wargs, n);
  XtManageChild(disc5);
  XmStringFree(s[0]);
  XtAddCallback(disc5, XmNarmCallback, new_drum_callback, (XtPointer) 150);

  n = 0;
  XtSetArg(wargs[n], XmNlabelString, (s[0]=XmStringCreateSimple("6 Discs"))); n++;
  disc6 = XmCreateToggleButton(new_radio1, "ToggleDisc6", wargs, n);
  XtManageChild(disc6);
  XmStringFree(s[0]);
  XtAddCallback(disc6, XmNarmCallback, new_drum_callback, (XtPointer) 180);

  n=0;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNrightAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, new_radio1); n++;
  XtSetArg(wargs[n], XmNtopOffset, 10); n++;
  XtSetArg(wargs[n], XmNorientation, XmHORIZONTAL); n++;
  XtSetArg(wargs[n], XmNshadowThickness, 3); n++;
  separator1 = XtCreateManagedWidget("separator1", xmSeparatorWidgetClass, new_form, wargs, n);

  n=0;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, separator1); n++;
  XtSetArg(wargs[n], XmNlabelString, (s[0]=XmStringCreateSimple("Select Buffer:"))); n++;
  new_label2 = XmCreateLabel(new_form, "DrumTitle", wargs, n);
  XtManageChild(new_label2);
  XmStringFree(s[0]);

  n=0;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, new_label2); n++;
  new_check1 = XmCreateSimpleCheckBox(new_form, "CheckBox1", wargs, n);
  XtManageChild(new_check1);

  n = 0;
  XtSetArg(wargs[n], XmNlabelString, (s[0]=XmStringCreateSimple("Create Buffer"))); n++;
  XtSetArg(wargs[n], XmNset, XmSET); n++;
  main_buffer1 = XmCreateToggleButton(new_check1, "ToggleBuffer", wargs, n);
  XtAddCallback(main_buffer1, XmNvalueChangedCallback, buffer_callback, NULL);
  XtManageChild(main_buffer1);
  XmStringFree(s[0]);

  for(i=0; i<MAXBDISKS; i++)
  {
    n=0;
    XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
    XtSetArg(wargs[n], XmNtopOffset, 4); n++;
    XtSetArg(wargs[n], XmNtopWidget, i==0?new_check1:bdisk_select[i-1]); n++;
    sprintf(tmp_label, "Select Bufferdisk %d:", i+8);
    XtSetArg(wargs[n], XmNlabelString, (s[0]=XmStringCreateSimple(tmp_label))); n++;
    sprintf(tmp_title, "BDiskTitle%d", i+1);
    bdisk_label[i] = XmCreateLabel(new_form, tmp_title, wargs, n);
    XtManageChild(bdisk_label[i]);
    XmStringFree(s[0]);

    n=0;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
    XtSetArg(wargs[n], XmNtopWidget, bdisk_label[i]); n++;
    XtSetArg(wargs[n], XmNleftOffset, 0); n++;
    XtSetArg(wargs[n], XmNtopOffset, -4); n++;
    XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
    XtSetArg(wargs[n], XmNleftWidget, bdisk_label[i]); n++;
    sprintf(tmp_title, "BDiskSelect%d", i+1);
    bdisk_select[i] = XmCreateOptionMenu(new_form, tmp_title, wargs, n);

    n=0;
    bdisk_pane[i] = XmCreatePulldownMenu(bdisk_select[i], "pulldown", wargs, n);

    for(j=0; bdisktypes[j].name!=NULL; j++)
    {
      n=0;
      sprintf(tmp_title, "BDiskSelect%d_%d", i+1, j+1);
      if(bdisktypes[j].size==0)
      {
	sprintf(tmp_label, "%s", bdisktypes[j].name);
      }
      else
      {
	sprintf(tmp_label, "%s: %d tracks of %d cells", bdisktypes[j].name, bdisktypes[j].size, bdisktypes[j].tracksize);
      }
      XtSetArg(wargs[n], XmNlabelString, (s[0]=XmStringCreateLocalized(tmp_label))); n++;
      bdisk_w[i][j] = XtCreateManagedWidget(tmp_title, xmPushButtonWidgetClass, bdisk_pane[i], wargs, n);
      XmStringFree(s[0]);
      XtAddCallback(bdisk_w[i][j], XmNactivateCallback, bdisk_select_callback, (XtPointer) ((long) (i*MAXBDISKTYPES+j)));
    }

    n=0;
    XtSetArg(wargs[n], XmNsubMenuId, bdisk_pane[i]); n++;
    XtSetValues(bdisk_select[i], wargs, n);

    XtManageChild(bdisk_select[i]);

    n=0;
    XtSetArg(wargs[n], XmNmenuHistory, bdisk_w[i][0]); n++;
    XtSetValues(bdisk_select[i], wargs, n);
    
  }

  n=0;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNrightAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, bdisk_pane[MAXBDISKS-1]); n++;
  XtSetArg(wargs[n], XmNtopOffset, 10); n++;
  XtSetArg(wargs[n], XmNorientation, XmHORIZONTAL); n++;
  XtSetArg(wargs[n], XmNshadowThickness, 3); n++;
  separator2 = XtCreateManagedWidget("separator2", xmSeparatorWidgetClass, new_form, wargs, n);

  n=0;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNleftOffset, 10); n++;
  XtSetArg(wargs[n], XmNbottomAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, separator2); n++;
  XtSetArg(wargs[n], XmNtopOffset, 10); n++;
  XtSetArg(wargs[n], XmNbottomOffset, 10); n++;
  new_ok = XtCreateManagedWidget("OK", xmPushButtonWidgetClass, new_form, wargs, n);
  XtAddCallback(new_ok, XmNactivateCallback, newok_callback, NULL);

  n=0;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNleftWidget, new_ok); n++;
  XtSetArg(wargs[n], XmNleftOffset, 10); n++;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, new_ok); n++;
  new_cancel = XtCreateManagedWidget("Cancel", xmPushButtonWidgetClass, new_form, wargs, n);
  XtAddCallback(new_cancel, XmNactivateCallback, newcancel_callback, NULL);

  /* init sound */

#ifdef HAS_OSS_SOUND
  sound_enabled = oss_sound_init();
#endif
#ifdef HAS_PA_SOUND
  sound_enabled = pa_sound_init();
#endif

  if(!sound_enabled)
  {
    XtVaSetValues(XtNameToWidget(main_optionsmenu, "button_3"), XmNset, False, NULL);
    XtVaSetValues(XtNameToWidget(main_optionsmenu, "button_3"), XmNsensitive, False, NULL);
  }
  else
  {
    XtVaSetValues(XtNameToWidget(main_optionsmenu, "button_3"), XmNset, True, NULL);
  }

  /* start everything */
  kb1_init();
  kb2_init();
  reader_init();
  printer_init();
  plotter_init();
  nimbi_init();
  punch_init();
  typewriter_init();
}

void interface_init(int argc, char **argv)
{
  int local_argc;
  char **local_argv;
  int i;

  /* Make a local copy of argc/argv */

  /* Kludge... */

  local_argc = argc+4;
  local_argv=malloc(sizeof(*local_argv)*(local_argc+1));
  for(i=0;i<argc; i++) local_argv[i] = argv[i];
  local_argv[local_argc-4] = "-title";
  local_argv[local_argc-3] = "GIER Simulator";
  local_argv[local_argc-2] = "-geometry";
  local_argv[local_argc-1] = "+10+10";
  local_argv[local_argc] = NULL;

  toplevel=XtAppInitialize(&app,"GIER", NULL, 0, &local_argc, local_argv, NULL, NULL, 0);

  main_wnd_init();

  normal_stop_pressed=0;
  normal_start_pressed=0;
  normal_singlestep_pressed=0;
  mikrotempi_stop_pressed=0;
  mikrotempi_start_pressed=0;
  HP_button_pressed=0;

  XtRealizeWidget(toplevel);
}

void GIER_stop()
{
  sound_sync();
  fprintf(stderr, "Time since start: %.2f sec.\n", ((double)(clock_count-last_start_clock_count))/((double)gier_clock));
  running=0;
}

void GIER_start()
{
  last_start_clock_count = clock_count;
  running=1;
}

void interface_update()
{
  if(mikrotempi_stop_pressed)
  {
    mikrotempi_stop_pressed=0;
    GIER_stop();
    Mode=Mode1;
    MA=1;
    h=0;
    typewriter_lowercase();
    drum_running=0;
    disk_status=0;
    TO_error=0;
    TR_error=0;
    BY = BY & ~512;
    YE_wait=0;
    kb12_reset();
    kb12_update();
    kb1_update();
    kb2_update();
    nimbi_update();
    kb12_reset();
  }
  else if(!running)
  {
    if(normal_start_pressed | normal_singlestep_pressed)
    {
      normal_start_pressed=0;
      GIER_start();
    }
    else if(mikrotempi_start_pressed)
    {
      mikrotempi_start_pressed=0;
      GIER_single_step();
      kb12_reset();
      kb12_update();
      kb1_update();
      kb2_update();
      nimbi_update();
      kb12_reset();
    }
    else if(HP_button_pressed && ((BY&512) == 0))
    {
      GIER_start();
    }
  }
}

void interface_run()
{
  XEvent event;
  int status;
  /*
  	If GIER is running, peek a message and dispatch,
	and run some microsteps.

	If GIER is halted, just wait for messages
  */

AGAIN:

  /* printf("AGAIN: demomode=%d running: %d YE_wait: %d typewriter_waiting: %d mikrotempi_stop_pressed: %d\n", demomode, running, YE_wait, typewriter_waiting, mikrotempi_stop_pressed); */
  status=XtAppPending(app);

  if(status)
  {
AGAIN2:
    /* printf("AGAIN2a: status=%d demomode=%d\n", status, demomode); */
    XtAppProcessEvent(app, status);
    /* printf("AGAIN2c: demomode=%d\n", demomode); */
    goto AGAIN;
  }
  if(running && ((!YE_wait)||demomode==2||is_drum_running()))
  {
    GIER_run();
    kb1_update();
    kb2_update();
    nimbi_update();
    reader_update();
    goto AGAIN;
  }
  kb12_reset();
  kb12_update();
  kb1_update();
  kb2_update();
  nimbi_update();
  status=XtIMAll;
  goto AGAIN2;
}

void interface_update_all()
{
  update_viewmenu();
  update_optionsmenu();
  update_aarhusmenu();
  update_testmenu();
  update_debugmenu();
  kb2_selected_update();
  kb12_reset();
  kb12_update();
  kb1_update();
  kb2_update();
  nimbi_update();
}

/* Local variables */

typedef struct
{
  Pixel lampcolors[8];
} kb1_ApplicationData, *kb1_ApplicationDataPtr;

static kb1_ApplicationData kb1_applicationdata;

/* Gamma 1.7 */
static XtResource kb1_resources[] =
{
  {"lampcolor0", "Lampcolor0", XtRPixel, sizeof(Pixel),
   XtOffset(kb1_ApplicationDataPtr, lampcolors[0]),
   XtRString, "#000000"},
  {"lampcolor1", "Lampcolor1", XtRPixel, sizeof(Pixel),
   XtOffset(kb1_ApplicationDataPtr, lampcolors[1]),
   XtRString, "#005100"},
  {"lampcolor2", "Lampcolor2", XtRPixel, sizeof(Pixel),
   XtOffset(kb1_ApplicationDataPtr, lampcolors[2]),
   XtRString, "#007A00"},
  {"lampcolor3", "Lampcolor3", XtRPixel, sizeof(Pixel),
   XtOffset(kb1_ApplicationDataPtr, lampcolors[3]),
   XtRString, "#009A00"},
  {"lampcolor4", "Lampcolor4", XtRPixel, sizeof(Pixel),
   XtOffset(kb1_ApplicationDataPtr, lampcolors[4]),
   XtRString, "#00B700"},
  {"lampcolor5", "Lampcolor5", XtRPixel, sizeof(Pixel),
   XtOffset(kb1_ApplicationDataPtr, lampcolors[5]),
   XtRString, "#00D100"},
  {"lampcolor6", "Lampcolor6", XtRPixel, sizeof(Pixel),
   XtOffset(kb1_ApplicationDataPtr, lampcolors[6]),
   XtRString, "#00E800"},
  {"lampcolor7", "Lampcolor7", XtRPixel, sizeof(Pixel),
   XtOffset(kb1_ApplicationDataPtr, lampcolors[7]),
   XtRString, "#00FF00"},
};

GIERword get_register()
{
  switch(selected_register)
  {
  case 0: /* R */
    return (AC&ONE0_39)|(H&ONE40_41);
  case 1: /* M */
    return (MQ&ONE0_39)|ONE40_41;
  case 2: /* O */
    return MD&ONE0_41;
  case 3: /* H */
    return (H&ONE0_39)|ONE40_41;
  case 4: /* L */
    return LI&ONE0_41;
  case 5: /* F */
    return (OR&ONE10_41)|(((GIERword) AD0)<<SHIFT9);
  case 6: /* r1 */
    return ((GIERword)OT)<<SHIFT9;
  case 7: /* s1 */
    return ((GIERword)SR)<<SHIFT9;
  case 8: /* r2 */
    return ((GIERword)AD1)<<SHIFT9;
  case 9: /* s2 */
    return ((GIERword)AD2)<<SHIFT9;
  case 10: /* in */
    return ((GIERword)IN)<<SHIFT9;
  case 11: /* ta */
    return ((GIERword)TA)<<SHIFT9;
  case 12: /* tk */
    return ((GIERword)TK)<<SHIFT9;
  case 13: /* bl */
    return ((GIERword)BL)<<SHIFT9;
  case 14: /* bs */
    return ((GIERword)BS)<<SHIFT9;
  case 15: /* by */
    return ((GIERword)BY)<<SHIFT9;
  }
  return 0;
}

void set_register(GIERword value)
{
  switch(selected_register)
  {
  case 0: /* R */
    AC = (AC&ONE00) | (value&ONE0_39);
    H = (H&ONE0_39) | (value&ONE40_41);
    break;
  case 1: /* M */
    MQ = value & ONE0_39;
    break;
  case 2: /* O */
    MD = value & ONE0_41;
    break;
  case 3: /* H */
    H = value & ONE0_39;
    break;
  case 4: /* L */
    LI = value & ONE0_41;
    break;
  case 5: /* F */
    OR = value & ONE10_41;
    AD0 = (value&ONE0_9)>>SHIFT9;
    break;
  case 6: /* r1 */
    OT = (value&ONE0_9)>>SHIFT9;
    break;
  case 7: /* s1 */
    SR = (value&ONE0_9)>>SHIFT9;
    break;
  case 8: /* r2 */
    AD1 = (value&ONE0_9)>>SHIFT9;
    break;
  case 9: /* s2 */
    AD2 = (value&ONE0_9)>>SHIFT9;
    break;
  case 10: /* in */
    IN = (value&ONE0_9)>>SHIFT9;
    break;
  case 11: /* ta */
    return;
  case 12: /* tk */
    TK = (value&ONE0_9)>>SHIFT9;
    break;
  case 13: /* bl */
    BL = (value&ONE0_9)>>SHIFT9;
    break;
  case 14: /* bs */
    BS = (value&ONE0_9)>>SHIFT9;
    break;
  case 15: /* by */
    BY = (value&ONE0_9)>>SHIFT9;
    break;
  }
}

static void kb12_reset()
{
  int i;
  if(debug&DEBUGinterface) fprintf(debug_fh, "kb12_reset\n");
  for(i=0; i<44; i++)
  {
    kb12_upper_count[i]=0;
    kb12_lower_count[i]=0;
  }
  for(i=0; i<10; i++)
  {
    kb12_aux_count[i]=0;
  }
  for(i=0; i<14; i++)
  {
    nimbi_count[i]=0;
  }
  kb12_count=0;
}

void kb12_update()
{
  GIERword upper,lower,mask;
  int i,auxstate,auxmask;
  /* Store state */

  if(kb1_wnd==NULL && kb2_wnd==NULL) return;

  kb1_getstate(&upper, &lower);
  mask = ONE000;

  for(i=0; i<44; i++)
  {
    if(upper&mask)
    {
      kb12_upper_count[i]+=COUNT_DELTA;
      if(kb12_upper_count[i]>MAX_COUNT) kb12_upper_count[i]=MAX_COUNT;
    }
    else
    {
      kb12_upper_count[i]=0;
    }
    if(i>1)
    {
      if(lower&mask)
      {
	kb12_lower_count[i-2]+=COUNT_DELTA;
	if(kb12_lower_count[i-2]>MAX_COUNT) kb12_lower_count[i-2]=MAX_COUNT;
      }
      else
      {
	kb12_lower_count[i-2]=0;
      }
    }
    mask = mask>>1;
  }

  kb2_getstate(&auxstate);

  auxmask=1;
  for(i=0; i<10; i++)
  {
    if(auxstate&auxmask)
    {
      kb12_aux_count[i]+=COUNT_DELTA;
      if(kb12_aux_count[i]>MAX_COUNT) kb12_aux_count[i]=MAX_COUNT;
    }
    else
    {
      kb12_aux_count[i]=0;
    }
    auxmask = auxmask<<1;
  }

  mask = ONE28;
  for(i=0; i<12; i++)
  {
    if(MQ&mask)
    {
      nimbi_count[i]+=COUNT_DELTA;
      if(nimbi_count[i]>MAX_COUNT) nimbi_count[i]=MAX_COUNT;
    }
    else
    {
      nimbi_count[i]=0;
    }
    mask = mask>>1;
  }
  if(!(IN&512))
  {
    nimbi_count[12]+=COUNT_DELTA;
    if(nimbi_count[12]>MAX_COUNT) nimbi_count[12]=MAX_COUNT;
  }
  else
  {
    nimbi_count[12]=0;
  }
  if(!(IN&256))
  {
    nimbi_count[13]+=COUNT_DELTA;
    if(nimbi_count[13]>MAX_COUNT) nimbi_count[13]=MAX_COUNT;
  }
  else
  {
    nimbi_count[13]=0;
  }

  kb12_count+=COUNT_DELTA;
  if(debug&DEBUGinterface) fprintf(debug_fh, "kb12_count: %d\n", kb12_count);
}

void kb1_getstate(GIERword *upper,GIERword *lower)
{
  /*
  	Generate upper bits:
  */
  *upper = 0ULL;

  if(spild) *upper |= ONE000;
  if(AC&ONE00) *upper |= ONE00;
  *upper |= ((GIERword)IN)<<SHIFT9;
  if(ind_ka) *upper |= ONE10;
  if(ind_kb) *upper |= ONE11;
  if(YE_wait) *upper |= ONE13;
  if(Mode == Mode1) *upper |= ONE14;
  if(Mode == Mode2) *upper |= ONE15;
  if(Mode == Mode3) *upper |= ONE16;
  if(Mode == Mode4) *upper |= ONE17;
  if(h) *upper |= ONE18;
#ifdef SHOW_DRUM_WAIT
  if(drum_wait) *upper |= ONE19;
#endif
  if(!running) *upper |= ONE20;
  if(TO_error) *upper |= ONE23;
  if(TR_error) *upper |= ONE24;
  if(reader_parity_error) *upper |= ONE25;
  *upper |= ONE41<<(15-selected_register);

  /*
  	Generate lower bits:

	If running, get the bus status,
	else the selected register.
  */

  if(running)
  {
    *lower = BUS&ONE0_41;
  }
  else
  {
    *lower = get_register();
  }
}

static int lamp_lookup(int intensity)
{
  if(kb12_count <=1)
    return(intensity?7:0);
  else
  {
    intensity = intensity/20;
    return(intensity>7?7:intensity);
  }
}

void kb1_update()
{
  double x;
  int i;

  if(kb1_wnd == NULL) return;
  if(kb1_being_destroyed) return;
  if(kb12_count==0) kb12_count=1;

  if(debug&DEBUGinterface) fprintf(debug_fh, "lower:");
  for(i=0; i<42; i++)
  {
    x = BIT0x + (BIT41x-BIT0x)/41.0*((double)i) + 0.5;
    XDrawLine(XtDisplay(kb1_drawing), XtWindow(kb1_drawing), kb1_gc[lamp_lookup(kb12_lower_count[i])], (int) x, kb1_low_y1, (int) x, kb1_low_y2);
    if(debug&DEBUGinterface) fprintf(debug_fh, " %d",kb12_lower_count[i]);
  }

  if(debug&DEBUGinterface) fprintf(debug_fh, "\nupper:");
  for(i=0; i<44; i++)
  {
    x = BITOx + (BITbyx-BITOx)/43.0*((double)i) + 0.5;

    XDrawLine(XtDisplay(kb1_drawing), XtWindow(kb1_drawing), kb1_gc[lamp_lookup(kb12_upper_count[i])], (int) x, kb1_high_y1, (int) x, kb1_high_y2);
    if(debug&DEBUGinterface) fprintf(debug_fh, " %d",kb12_upper_count[i]);
  }
  if(debug&DEBUGinterface) fprintf(debug_fh, "\n");
}

static void kb1_expose_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  if(debug&DEBUGinterface) fprintf(debug_fh, "kb1_expose_callback called.\n");
  kb1_update();
}

static void kb1_input_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  int i;
  int changed=0;
  XmDrawingAreaCallbackStruct *cbstruct = (XmDrawingAreaCallbackStruct *) call_data;
  GIERword lower,mask;
#define THISBUTTON(bx,by) ( ABS(event->xbutton.x-(bx))<10 && ABS(event->xbutton.y-(by))<10 )
  XEvent *event = cbstruct -> event;
  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "kb1_input_callback called.\n");
    fprintf(debug_fh, "event type: %d\n", event->type);
  }
  if(event->type == ButtonRelease)
  {
    lower = get_register();
    if(debug&DEBUGinterface) fprintf(debug_fh, "x: %d y: %d button: %d state: %d\n", event->xbutton.x, event->xbutton.y, event->xbutton.button, event->xbutton.state);
    mask = ONE0;
    if(event->xbutton.button==1)
    {
      /* left mouse button */
      for(i=0; i<42; i++)
      {
	if(THISBUTTON(lowerzero[i].x, lowerzero[i].y))
	{
	  lower = lower & ~(mask);
	  changed=1;
	}
	else if(THISBUTTON(lowerone[i].x, lowerone[i].y))
	{
	  lower = lower | mask;
	  changed=1;
	}
	mask = mask>>1;
      }
      if(THISBUTTON(BITall_0x, BITall_0y))
      {
	lower = 0ULL;
	changed=1;
      }
      else if(THISBUTTON(BITall_1x, BITall_1y))
      {
	lower = 0x3ffffffffffULL;
	changed=1;
      }
      if(changed)
      {
	set_register(lower);
      }
      else if(THISBUTTON(T_0x, T_0y))
      {
	AC = (AC & ~ONE00);
	changed=1;
      }
      else if(THISBUTTON(T_1x, T_1y))
      {
	AC = AC | ONE00;
	changed=1;
      }
      else if(THISBUTTON(kb1_KA_0x, kb1_KA_0y))
      {
	ind_ka = 0;
	changed=1;
      }
      else if(THISBUTTON(kb1_KA_1x, kb1_KA_1y))
      {
	ind_ka = 1;
	changed=1;
      }
      else if(THISBUTTON(kb1_KB_0x, kb1_KB_0y))
      {
	ind_kb = 0;
	changed=1;
      }
      else if(THISBUTTON(kb1_KB_1x, kb1_KB_1y))
      {
	ind_kb = 1;
	changed=1;
      }
      else if(THISBUTTON(Normal_start_x,Normal_start_y))
      {
	normal_start_pressed=1;
	if(debug&DEBUGinterface) fprintf(debug_fh, "Normal start pressed.\n");
	interface_update();
      }
      else if(THISBUTTON(Normal_stop_x,Normal_stop_y))
      {
        normal_stop_pressed=1;
	if(debug&DEBUGinterface) fprintf(debug_fh, "Normal stop pressed.\n");
	interface_update();
      }
      else if(THISBUTTON(Mikrotempi_start_x,Mikrotempi_start_y))
      {
        mikrotempi_start_pressed=1;
	if(debug&DEBUGinterface) fprintf(debug_fh, "Mikrotempi start pressed.\n");
	interface_update();
      }
      else if(THISBUTTON(Mikrotempi_stop_x,Mikrotempi_stop_y))
      {
        mikrotempi_stop_pressed=1;
	if(debug&DEBUGinterface) fprintf(debug_fh, "Mikrotempi stop pressed.\n");
	interface_update();
      }
      else if(THISBUTTON(Switch_x,Switch_y))
      {
	if(debug&DEBUGinterface) fprintf(debug_fh, "Switch pressed.\n");
	if(confirm(kb1_wnd,"OK to exit GIER simulator?")) GIER_exit();
      }
      else
      {
	for(i=0; i<16; i++)
	{
	  if(THISBUTTON(select_register[i].x, select_register[i].y))
	  {
	    selected_register=i;
	    changed=1;
	    break;
	  }
	}
      }
      if(changed)
      {
        kb12_reset();
	kb12_update();
	kb1_update();
	kb2_update();
	nimbi_update();
        kb12_reset();
      }
    }
    else if(event->xbutton.button==3)
    {
      /* Right mouse button */
      if(THISBUTTON(Normal_start_x,Normal_start_y))
      {
        /* Right click on Normal start: single step. */
        normal_singlestep_pressed=1;
	if(debug&DEBUGinterface) fprintf(debug_fh, "Normal start&stop pressed (single step).\n");
	interface_update();
      }
    }
  }
}

static void kb1_resize_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  if(debug&DEBUGinterface) fprintf(debug_fh, "kb1_resize_callback called.\n");
}

static void kb1_destroy_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  kb1_wnd = NULL;

  if(debug&DEBUGinterface) fprintf(debug_fh,"kb1_destroy_callback called.\n");
  update_viewmenu();
}

void kb1_init()
{
  int st;
  int i;
  double x;
  XGCValues values;
  XtGCMask valueMask;
  Window root_window;
  Pixmap bg,mask;
  XpmAttributes attrib;

  if(kb1_wnd != NULL)
  {
    XRaiseWindow(XtDisplay(kb1_wnd), XtWindow(kb1_wnd));
  }
  else
  {
    /* Get resources */

    kb1_being_destroyed = 0;
    XtGetApplicationResources(toplevel, &kb1_applicationdata,
      kb1_resources, XtNumber(kb1_resources), NULL, 0);

    n=0;
    XtSetArg(wargs[n], XtNtitle, "GIER Main Control Board"); n++;
    XtSetArg(wargs[n], XtNgeometry, "-20+20"); n++;
    kb1_wnd = XtAppCreateShell("kb1", "kb1", topLevelShellWidgetClass, XtDisplay(toplevel), wargs, n);
    XtAddCallback(kb1_wnd, XtNdestroyCallback, kb1_destroy_callback, NULL);

    n=0;
    kb1_form=XtCreateManagedWidget("kb1_form", xmFormWidgetClass, kb1_wnd, wargs, n);

    n=0;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNrightAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNbottomAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNwidth, 850); n++;
    XtSetArg(wargs[n], XmNheight, 549); n++;
    kb1_drawing=XtCreateManagedWidget("kb1_drawing", xmDrawingAreaWidgetClass, kb1_form, wargs, n);

    for(i=0; i<42; i++)
    {
      x = BIT0_0x + (BIT41_0x-BIT0_0x)/41.0*((double)i) + 0.5;
      if(i%2 == 0)
      {
	lowerzero[i].x = (int) x;
	lowerzero[i].y = BIT0_0y;

	lowerone[i].x = (int) x;
	lowerone[i].y = BIT0_1y;
      }
      else
      {
	lowerzero[i].x = (int) x;
	lowerzero[i].y = BIT1_0y;

	lowerone[i].x = (int) x;
	lowerone[i].y = BIT1_1y;
      }
    }

    for(i=0; i<16; i++)
    {
      x = R_x + (by_x-R_x)/15.0*((double)i) + 0.5;
      select_register[i].x = x;
      if(i%2 == 0)
      {
        select_register[i].y = R_y;
      }
      else
      {
        select_register[i].y = M_y;
      }
    }

    attrib.valuemask = XpmCloseness;
    attrib.closeness = 20000;
    root_window=XRootWindow(XtDisplay(kb1_drawing),XDefaultScreen(XtDisplay(kb1_drawing)));
    st=XpmCreatePixmapFromData(XtDisplay(kb1_drawing), root_window, kb1_xpm, &bg, NULL, &attrib);
    if(st != XpmSuccess)
    {
      if(debug&DEBUGinterface) fprintf(debug_fh, "XpmError: %s\n", XpmGetErrorString(st));
    }
    

    n=0;
    XtSetArg(wargs[n], XmNbackgroundPixmap, bg); n++;
    XtSetValues(kb1_drawing, wargs, n);

    XtAddCallback(kb1_drawing, XmNexposeCallback, kb1_expose_callback, (XtPointer) NULL);
    XtAddCallback(kb1_drawing, XmNinputCallback, kb1_input_callback, (XtPointer) NULL);
    XtAddCallback(kb1_drawing, XmNresizeCallback, kb1_resize_callback, (XtPointer) NULL);

    for(i=0;i<8;i++)
    {
      valueMask = GCForeground | GCBackground | GCLineWidth;
      values.foreground = WhitePixel(XtDisplay(kb1_drawing),XDefaultScreen(XtDisplay(kb1_drawing)));
      values.foreground = kb1_applicationdata.lampcolors[i];
      values.background = BlackPixel(XtDisplay(kb1_drawing),XDefaultScreen(XtDisplay(kb1_drawing)));
      values.line_width = 4;
      kb1_gc[i] = XtGetGC(kb1_drawing, valueMask, &values);
    }

    XtRealizeWidget(kb1_wnd);

    selected_register = 0;
  }
}

void kb1_destroy()
{
  if(debug&DEBUGinterface) fprintf(debug_fh,"kb1_destroy called.\n");
  kb1_being_destroyed=1;
  XtUnrealizeWidget(kb1_wnd);
  XtDestroyWidget(kb1_wnd);
}

int kb1_is_visible()
{
  return(kb1_wnd!=NULL);
}

/* Local variables */

typedef struct
{
  Pixel lampcolors[8];
} kb2_ApplicationData, *kb2_ApplicationDataPtr;

static kb2_ApplicationData kb2_applicationdata;

static XtResource kb2_resources[] =
{
  {"lampcolor0", "Lampcolor0", XtRPixel, sizeof(Pixel),
   XtOffset(kb2_ApplicationDataPtr, lampcolors[0]),
   XtRString, "#000000"},
  {"lampcolor1", "Lampcolor1", XtRPixel, sizeof(Pixel),
   XtOffset(kb2_ApplicationDataPtr, lampcolors[1]),
   XtRString, "#005100"},
  {"lampcolor2", "Lampcolor2", XtRPixel, sizeof(Pixel),
   XtOffset(kb2_ApplicationDataPtr, lampcolors[2]),
   XtRString, "#007A00"},
  {"lampcolor3", "Lampcolor3", XtRPixel, sizeof(Pixel),
   XtOffset(kb2_ApplicationDataPtr, lampcolors[3]),
   XtRString, "#009A00"},
  {"lampcolor4", "Lampcolor4", XtRPixel, sizeof(Pixel),
   XtOffset(kb2_ApplicationDataPtr, lampcolors[4]),
   XtRString, "#00B700"},
  {"lampcolor5", "Lampcolor5", XtRPixel, sizeof(Pixel),
   XtOffset(kb2_ApplicationDataPtr, lampcolors[5]),
   XtRString, "#00D100"},
  {"lampcolor6", "Lampcolor6", XtRPixel, sizeof(Pixel),
   XtOffset(kb2_ApplicationDataPtr, lampcolors[6]),
   XtRString, "#00E800"},
  {"lampcolor7", "Lampcolor7", XtRPixel, sizeof(Pixel),
   XtOffset(kb2_ApplicationDataPtr, lampcolors[7]),
   XtRString, "#00FF00"},
};

static void kb2_draw_bit(int y, int intensity)
{
  XDrawLine(XtDisplay(kb2_drawing), XtWindow(kb2_drawing), kb2_gc[lamp_lookup(intensity)], kb2_low_x1, y, kb2_low_x2, y);
}

void kb2_getstate(int *state)
{
  *state = 0;
  if(ind_ka) *state |= 1;
  if(ind_kb) *state |= 2;
  if(!running) *state |= 4;
  if(YE_wait) *state |= 8;
  if(reader_parity_error) *state |= 16;
  if(TR_error) *state |= 32;
  if(TO_error) *state |= 64;
  if(OR&ONE27) *state |= 128;
  if(BY&512) *state |= 256;
  if(is_drum_running()) *state |= 512;
}

void kb2_update()
{
  double x;
  int i;

  if(kb2_wnd == NULL) return;
  if(kb2_being_destroyed) return;
  if(kb12_count==0) kb12_count=1;

  kb2_draw_bit(kb2_KA_y, kb12_aux_count[0]);
  kb2_draw_bit(kb2_KB_y, kb12_aux_count[1]);
  kb2_draw_bit(Klar_y, kb12_aux_count[2]);
  kb2_draw_bit(YE_y, kb12_aux_count[3]);
  kb2_draw_bit(Tape_Parity_y, kb12_aux_count[4]);
  kb2_draw_bit(Drum_Parity_y, kb12_aux_count[5]);
  kb2_draw_bit(TO_y, kb12_aux_count[6]);
  kb2_draw_bit(iloop_y, kb12_aux_count[7]);
  kb2_draw_bit(HP_inhib_y, kb12_aux_count[8]);
  /* kb2_draw_bit(drumbusy_y, kb12_aux_count[9]); */
}

void kb2_selected_update()
{
  int i,j;
  if(kb2_wnd == NULL) return;
  for(i=1; i<5; i++)
  for(j=1; j<5; j++)
  {
    n=0;
    XtSetArg(wargs[n], XmNset, (selected_out[i]&(1<<(j+2)))?True:False); n++;
    XtSetValues(select_out[i][j], wargs, n);
  }
}

static void selection_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  XmToggleButtonCallbackStruct *tgb = (XmToggleButtonCallbackStruct *) call_data;
  int i,j,value,mask;
  i=((long) client_data)/5;
  j=((long) client_data)%5;
  value = 1<<(j+2);
  mask = ~value;

#if XmVERSION==1
  if(!tgb->set) value=0;
#else
  if(tgb->set == XmUNSET) value=0;
#endif

  selected_out[i] = (selected_out[i] & mask) | value;
  if(debug&DEBUGinterface) fprintf(debug_fh, "selection_callback called. i:%d j:%d value:%d\n", i, j, value);
}

static void kb2_expose_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  if(debug&DEBUGinterface) fprintf(debug_fh, "kb2_expose_callback called.\n");
  kb2_update();
}

static void kb2_input_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  int i;
  int changed=0;
  XmDrawingAreaCallbackStruct *cbstruct = (XmDrawingAreaCallbackStruct *) call_data;
  XEvent *event = cbstruct -> event;
  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "kb2_input_callback called.\n");
    fprintf(debug_fh, "event type: %d\n", event->type);
  }
  if(event->type == ButtonRelease)
  {
    if(debug&DEBUGinterface) fprintf(debug_fh, "x: %d y: %d button: %d state: %d\n", event->xbutton.x, event->xbutton.y, event->xbutton.button, event->xbutton.state);
    if(event->xbutton.button==1)
    {
      /* left mouse button */
      if(THISBUTTON(kb2_KA_0x, kb2_KA_0y))
      {
	ind_ka = 0;
	changed=1;
      }
      else if(THISBUTTON(kb2_KA_1x, kb2_KA_1y))
      {
	ind_ka = 1;
	changed=1;
      }
      else if(THISBUTTON(kb2_KB_0x, kb2_KB_0y))
      {
	ind_kb = 0;
	changed=1;
      }
      else if(THISBUTTON(kb2_KB_1x, kb2_KB_1y))
      {
	ind_kb = 1;
	changed=1;
      }
      else if(THISBUTTON(RESET_x, RESET_y))
      {
	mikrotempi_stop_pressed=1;
	if(debug&DEBUGinterface) fprintf(debug_fh, "Mikrotempi stop pressed.\n");
	interface_update();
      }
      else if(THISBUTTON(HP_x, HP_y))
      {
	HP_button_pressed=1;
	if(debug&DEBUGinterface) fprintf(debug_fh, "HP button pressed.\n");
	interface_update();
      }
    } /* Left button */
    else if(event->xbutton.button==3)
    {
      /* Right button; right-click on HP is like
	 pressing on HP button while holding normal stop */
      if(THISBUTTON(HP_x, HP_y))
      {
	HP_button_pressed=1;
	normal_stop_pressed=1;
	if(debug&DEBUGinterface) fprintf(debug_fh, "HP&stop button pressed.\n");
	interface_update();
      }
    
    }
    if(changed)
    {
      kb12_reset();
      kb12_update();
      kb1_update();
      kb2_update();
      kb12_reset();
    }
  } /* button release */
}

static void kb2_resize_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  if(debug&DEBUGinterface) fprintf(debug_fh, "kb2_resize_callback called.\n");
}

static void kb2_destroy_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  kb2_wnd = NULL;

  update_viewmenu();
}

void kb2_init()
{
  int st;
  int i,j;
  double x;
  XGCValues values;
  XtGCMask valueMask;
  char button_name[20];
  char button_text[20];
  XmString stmp;
  Window root_window;
  Pixmap bg,mask;
  XpmAttributes attrib;

  if(kb2_wnd != NULL)
  {
    XRaiseWindow(XtDisplay(kb2_wnd), XtWindow(kb2_wnd));
  }
  else
  {
    /* Get resources */

    kb2_being_destroyed = 0;
    XtGetApplicationResources(toplevel, &kb2_applicationdata,
    kb2_resources, XtNumber(kb2_resources), NULL, 0);

    n=0;
    XtSetArg(wargs[n], XtNtitle, "GIER Aux. Control Board"); n++;
    XtSetArg(wargs[n], XtNgeometry, "-20-100"); n++;
    kb2_wnd = XtAppCreateShell("kb2", "kb2", topLevelShellWidgetClass, XtDisplay(toplevel), wargs, n);
    XtAddCallback(kb2_wnd, XtNdestroyCallback, kb2_destroy_callback, (XtPointer)NULL);

    n=0;
    kb2_form=XtCreateManagedWidget("kb2_form", xmFormWidgetClass, kb2_wnd, wargs, n);

    n=0;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNrightAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNwidth, 386); n++;
    XtSetArg(wargs[n], XmNheight, 284); n++;
    kb2_drawing=XtCreateManagedWidget("kb2_drawing", xmDrawingAreaWidgetClass, kb2_form, wargs, n);

    attrib.valuemask = XpmCloseness;
    attrib.closeness = 20000;
    root_window=XRootWindow(XtDisplay(kb2_drawing),XDefaultScreen(XtDisplay(kb2_drawing)));
    st=XpmCreatePixmapFromData(XtDisplay(kb2_drawing), root_window, kb2_xpm, &bg, NULL, &attrib);
    if(st != XpmSuccess)
    {
      if(debug&DEBUGinterface) fprintf(debug_fh, "XpmError: %s\n", XpmGetErrorString(st));
    }
  

    n=0;
    XtSetArg(wargs[n], XmNbackgroundPixmap, bg); n++;
    XtSetValues(kb2_drawing, wargs, n);

    XtAddCallback(kb2_drawing, XmNexposeCallback, kb2_expose_callback, (XtPointer) NULL);
    XtAddCallback(kb2_drawing, XmNinputCallback, kb2_input_callback, (XtPointer) NULL);
    XtAddCallback(kb2_drawing, XmNresizeCallback, kb2_resize_callback, (XtPointer) NULL);

    for(i=0;i<8;i++)
    {
      valueMask = GCForeground | GCBackground | GCLineWidth;
      values.foreground = WhitePixel(XtDisplay(kb2_drawing),XDefaultScreen(XtDisplay(kb2_drawing)));
      values.foreground = kb2_applicationdata.lampcolors[i];
      values.background = BlackPixel(XtDisplay(kb2_drawing),XDefaultScreen(XtDisplay(kb2_drawing)));
      values.line_width = 4;
      kb2_gc[i] = XtGetGC(kb2_drawing, valueMask, &values);
    }

    /*
	Create control panel to map BY values to devices:
    */

    for(i=0; i<5; i++)
    for(j=0; j<5; j++)
    {
      n=0;
      XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
      if(i==0)
      {
	XtSetArg(wargs[n], XmNtopWidget, kb2_drawing); n++;
      }
      else
      {
	XtSetArg(wargs[n], XmNtopWidget, select_out[i-1][1]); n++;
	if(j==0)
	{
	  XtSetArg(wargs[n], XmNtopOffset, 9); n++;
	}
      }
      if(i==4)
      {
	/* XtSetArg(wargs[n], XmNbottomAttachment, XmATTACH_FORM); n++; */
      }
      if(j==0)
      {
	XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
	XtSetArg(wargs[n], XmNleftOffset, 80); n++;
      }
      else
      {
	if(i==0)
	{
	  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
	  XtSetArg(wargs[n], XmNleftWidget, select_out[0][j-1]); n++;
	}
	else
	{
	  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
	  XtSetArg(wargs[n], XmNleftWidget, select_out[0][j]); n++;
	}
      }
      sprintf(button_name, "button_%d_%d", i+1, j+1);
      if(i==0)
      {
	/* Title row */
	if(j>0)
	{
	  sprintf(button_text, "%3d   ", 1<<(j+2));
	}
	else
	{
	  sprintf(button_text, "          ");
	}
	XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple(button_text))); n++;
	select_out[i][j] = XmCreateLabel(kb2_form, button_name, wargs, n);
	XtManageChild(select_out[i][j]);
	XmStringFree(stmp);
      }
      else
      {
	if(j==0)
	{
	  switch(i)
	  {
	  case 1:
	    sprintf(button_text, "Printer   ");
	    break;
	  case 2:
	    sprintf(button_text, "Typewriter");
	    break;
	  case 3:
	    sprintf(button_text, "Punch     ");
	    break;
	  case 4:
	    sprintf(button_text, "Plotter   ");
	    break;
	  }
	  XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple(button_text))); n++;
	  select_out[i][j] = XmCreateLabel(kb2_form, button_name, wargs, n);
	  XtManageChild(select_out[i][j]);
	  XmStringFree(stmp);
	}
	else
	{
	  XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple(""))); n++;
	  XtSetArg(wargs[n], XmNset, (selected_out[i]&(1<<(j+2)))?True:False); n++;
#if XmVERSION==1
	  XtSetArg(wargs[n], XmNindicatorOn, True); n++;
#else
	  XtSetArg(wargs[n], XmNindicatorOn, XmINDICATOR_CHECK_BOX); n++;
#endif
	  XtSetArg(wargs[n], XmNindicatorSize, 20); n++;
	  select_out[i][j] = XmCreateToggleButton(kb2_form, button_name, wargs, n);
	  XtAddCallback(select_out[i][j], XmNvalueChangedCallback, selection_callback, (XtPointer) ((long)(i*5+j)));
	  XtManageChild(select_out[i][j]);
	  XmStringFree(stmp);
	}
      }
    }
    XtRealizeWidget(kb2_wnd);
  }
}

void kb2_destroy()
{
  if(debug&DEBUGinterface) fprintf(debug_fh,"kb2_destroy called.\n");
  kb2_being_destroyed=1;
  XtUnrealizeWidget(kb2_wnd);
  XtDestroyWidget(kb2_wnd);
}

int kb2_is_visible()
{
  return(kb2_wnd!=NULL);
}

/* Local variables */

typedef struct
{
  XFontStruct *textfont;
  XFontStruct *subscriptfont;
  XFontStruct *symbolfont;
  Pixel lampcolor, redcolor, blackcolor;

} typewriter_ApplicationData, *typewriter_ApplicationDataPtr;

static typewriter_ApplicationData typewriter_applicationdata;

static XtResource typewriter_resources[] =
{
  {"textfont", "Textfont", XtRFontStruct, sizeof(XFontStruct *),
   XtOffset(typewriter_ApplicationDataPtr, textfont),
#ifdef GIANTTYPEWRITER
   XtRString, "-adobe-courier-bold-r-*-*-*-240-100-100-*-*-*-*"
#else
   XtRString, "-adobe-courier-medium-r-*-*-*-180-75-75-*-*-*-*"
#endif
  },
  {"subscriptfont", "Subscriptfont", XtRFontStruct, sizeof(XFontStruct *),
   XtOffset(typewriter_ApplicationDataPtr, subscriptfont),
#ifdef GIANTTYPEWRITER
   XtRString, "-adobe-courier-bold-r-*-*-*-100-100-100-*-*-*-*"
#else
   XtRString, "-adobe-courier-medium-r-*-*-*-100-75-75-*-*-*-*"
#endif
  },
  {"symbolfont", "Symbolfont", XtRFontStruct, sizeof(XFontStruct *),
   XtOffset(typewriter_ApplicationDataPtr, symbolfont),
#ifdef GIANTTYPEWRITER
   XtRString, "-adobe-symbol-medium-r-*-*-*-240-100-100-*-*-*-*"
#else
   XtRString, "-adobe-symbol-medium-r-*-*-*-180-75-75-*-*-*-*"
#endif
  },
  {"lampcolor", "Lampcolor", XtRPixel, sizeof(Pixel),
   XtOffset(typewriter_ApplicationDataPtr, lampcolor),
   XtRString, "green"
  },
  {"redcolor", "Redcolor", XtRPixel, sizeof(Pixel),
   XtOffset(typewriter_ApplicationDataPtr, redcolor),
   XtRString, "red"
  },
  {"blackcolor", "Blackcolor", XtRPixel, sizeof(Pixel),
   XtOffset(typewriter_ApplicationDataPtr, blackcolor),
   XtRString, "black"
  },
}; 

static void typewriter_drawchar(int row, int col)
{
  short c;
  int overprint;
  Position x,y;
  Dimension width, height;
  char text[3];
  int tlength;
  int ired, ifont;

  overprint=0;

  x = typewriter_CursorX(col);
  y = typewriter_CursorY(row);

  XDrawImageString(XtDisplay(typewriter_drawing), XtWindow(typewriter_drawing), gc_table[0][0], x, y, " ", 1);

  while( (overprint<MAXOVERWRITE) &&
	 (c=paper[row][col][overprint++]) != 0)
  {
    ired = (c&RED_MASK)!=0;
    ifont = (c&SYMBOL_MASK)!=0;
    text[0] = (char) (c&255);
    tlength=1;

    if(text[0]=='\'')
    {
      /* subscript 10 */
      ifont = 2;
      text[0] = '1';
      text[1] = '0';
      tlength=2;

      y=y+(typewriter_applicationdata.textfont->descent-typewriter_applicationdata.subscriptfont->descent);
    }

    if(overprint == 1)
      XDrawImageString(XtDisplay(typewriter_drawing), XtWindow(typewriter_drawing), gc_table[ired][ifont], x, y, text, tlength);
    else
      XDrawString(XtDisplay(typewriter_drawing), XtWindow(typewriter_drawing), gc_table[ired][ifont], x, y, text, tlength);
  }
}

static void typewriter_update(int row1,int row2,int col1, int col2)
{
  int row,col;

  if(typewriter_wnd == NULL) return;

  if(row1<0) row1=0;
  if(row2>=LINEBUFFER) row2=LINEBUFFER-1;

  if(col1<0) col1=0;
  if(col2>=PAPERWIDTH) col2=PAPERWIDTH-1;

  for(row=row1; row<=row2; row++)
  for(col=col1; col<=col2; col++)
  typewriter_drawchar(row,col);
}

static void storechar(short c)
{
  int overwrite;
  if(redtext) c |= RED_MASK;
  overwrite=0;
  while(paper[cur_row][cur_col][overwrite]!=0 && overwrite<(MAXOVERWRITE-1) ) overwrite++;
  paper[cur_row][cur_col][overwrite] = c;
  cur_col++;
  if(cur_col >= PAPERWIDTH) cur_col=PAPERWIDTH-1;
}

static void typewriter_scroll()
{
  int i,j,k;

  for(i=0; i<LINEBUFFER-1; i++)
  for(j=0; j<PAPERWIDTH; j++)
  for(k=0; k<MAXOVERWRITE; k++)
  paper[i][j][k] = paper[i+1][j][k];

  for(j=0; j<PAPERWIDTH; j++)
  for(k=0; k<MAXOVERWRITE; k++)
  paper[LINEBUFFER-1][j][k] = 0;


  XCopyArea(XtDisplay(typewriter_drawing), XtWindow(typewriter_drawing), XtWindow(typewriter_drawing), scroll_gc,
	    typewriter_CursorX(0), typewriter_CursorY(1),
	    typewriter_CursorX(PAPERWIDTH)-typewriter_CursorX(0), typewriter_CursorY(LINEBUFFER)-typewriter_CursorY(1),
	    typewriter_CursorX(0), typewriter_CursorY(0));

  in_copy++;
  typewriter_copywait();

  if(debug&DEBUGinterface) fprintf(debug_fh, "XCopyArea: (%d,%d) (%d,%d) (%d,%d)\n",
	    typewriter_CursorX(0), typewriter_CursorY(1),
	    typewriter_CursorX(PAPERWIDTH)-typewriter_CursorX(0), typewriter_CursorY(LINEBUFFER)-typewriter_CursorY(1),
	    typewriter_CursorX(0), typewriter_CursorY(0));

	  
}

void typewriter_SY(char c)
{
  short ch2;
  int this_col;
  int ccase;

  if(debug&DEBUGinterface) fprintf(debug_fh, "typewriter_SY: %d\n", c);
  if(c==64)
  {
#ifdef TYPEWRITER_SOUND
    if(sound_enabled)
    {
      if(cur_col == 0)
      {
	pa_simple_write(pa_s, typewriter_sound_vognretur_kort, typewriter_sound_vognretur_kort_len, &pa_error);
      }
      else
      {
	pa_simple_write(pa_s, typewriter_sound_vognretur_lang1, typewriter_sound_vognretur_lang1_len, &pa_error);
	if(cur_col>40)
	pa_simple_write(pa_s, typewriter_sound_vognretur_lang2, typewriter_sound_vognretur_lang2_len, &pa_error);
	pa_simple_write(pa_s, typewriter_sound_vognretur_lang2, (typewriter_sound_vognretur_lang2_len*(cur_col%40)/40)&(~1), &pa_error);
	pa_simple_write(pa_s, typewriter_sound_vognretur_lang3, typewriter_sound_vognretur_lang3_len, &pa_error);
      }
    }
#endif
    cur_col=0;
    cur_row++;
    if(cur_row==LINEBUFFER)
    {
      typewriter_scroll();
      cur_row--;
    }
  }
  else if(c<64)
  {
    ch2 = flx2a(c, &uppercase);
    this_col = cur_col;

#ifdef TYPEWRITER_SOUND
    if(sound_enabled)
    {
      if(c == 0)
      {
	pa_simple_write(pa_s, typewriter_sound_spaces[typewriter_sound_spaces_count++], typewriter_sound_space1_len, &pa_error);
	if(typewriter_sound_spaces[typewriter_sound_spaces_count]==NULL) typewriter_sound_spaces_count=0;
      }
      else if(c==58)
      {
	pa_simple_write(pa_s, typewriter_sound_lc, typewriter_sound_lc_len, &pa_error);
      }
      else if(c==60)
      {
	pa_simple_write(pa_s, typewriter_sound_uc, typewriter_sound_uc_len, &pa_error);
      }
      else if(c != 29 && c != 62 && c != 63)
      {
	pa_simple_write(pa_s, typewriter_sound_typers[typewriter_sound_typers_count++], typewriter_sound_typer1_len, &pa_error);
	if(typewriter_sound_typers[typewriter_sound_typers_count]==NULL) typewriter_sound_typers_count=0;
      }
    }
#endif
    if(debug&DEBUGinterface) fprintf(debug_fh, "c: %d ch2: %d\n", (int) c, (int) ch2);
  
    if(c==29)
      redtext=1;
    else if(c==62)
      redtext=0;
    else
    {
      if(ch2==163) /* £ */
	ch2=218|SYMBOL_MASK;
      else if(ch2=='*')
	ch2=180|SYMBOL_MASK;
      else if(ch2=='&')
	ch2=217|SYMBOL_MASK;

      if(debug&DEBUGinterface) fprintf(debug_fh, "ch2: %d\n", (int) ch2);

      if(ch2 != 255) storechar(ch2);
      if(c==14) cur_col--;
    }
    if(typewriter_wnd == NULL) return;
    typewriter_drawchar(cur_row,this_col);
    XFlush(XtDisplay(typewriter_drawing));
  }
  if(demomode==3)
  {
    ccase = c;
    if(uppercase) ccase+=128;
    if(ccase==demochar)
    {
      demomode=1;
      GIERdemocommand();
    }
  }
}

void typewriter_wait(int status)
{
  XmString stmp;
  int old_status;

  old_status = typewriter_waiting;

  if(status)
  {
    YE_wait=1;
    typewriter_waiting=1;
    if(demomode==4)
    {
      demomode=1;
      GIERdemocommand();
    }
  }
  else
  {
    YE_wait=0;
    typewriter_waiting=0;
  }

  if( (old_status != typewriter_waiting) && typewriter_is_visible())
  {
    n=0;
    if(typewriter_waiting)
    {
      XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple("INPUT"))); n++;
    }
    else
    {
      XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple("     "))); n++;
    }
    XtSetValues(typewriter_input_label, wargs, n);
    XmStringFree(stmp);
  }
}

static void typewriter_expose2_callback(Widget w, XtPointer client_data, XEvent *event, Boolean *cont)
{
  int row1, row2;
  int col1, col2;

  if(event->type == NoExpose)
  {
    row1 = 0;
    col1 = 0;
    row2 = LINEBUFFER-1;
    col2 = PAPERWIDTH-1;
  }
  else if(event->type == Expose || event->type == GraphicsExpose)
  {
    row1=event->xexpose.y/typewriter_font_height;
    row2=(event->xexpose.y+event->xexpose.height-1)/typewriter_font_height;
    col1=event->xexpose.x/typewriter_font_width;
    col2=(event->xexpose.x+event->xexpose.width-1)/typewriter_font_width;
  }
  typewriter_update(row1,row2,col1,col2);
  if(in_copy)
  {
    if((event->type == NoExpose) ||
       ((XExposeEvent *)event)->count == 0)
    {
      in_copy = 0;
    }
  }
}

static void typewriter_expose1_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  Boolean dummy;
  XmDrawingAreaCallbackStruct *cbstruct = (XmDrawingAreaCallbackStruct *) call_data;
  XEvent *event = cbstruct->event;
  typewriter_expose2_callback(w, client_data, event, &dummy);
}

static void typewriter_copywait()
{
  XEvent reply;
  XEvent *rep = &reply;
  Boolean cont;
  /*
	Wait for all events generated
	by XCopyArea.
  */

  while(in_copy)
  {
    cont = 1;
    XWindowEvent(XtDisplay(typewriter_drawing), XtWindow(typewriter_drawing), ExposureMask, &reply);
    switch(reply.type)
    {
    case Expose:
    case NoExpose:
    case GraphicsExpose:
      typewriter_expose2_callback(typewriter_drawing, NULL, &reply, &cont);
      break;
    }
  }
}

static void savechar(char c)
{
  if( ((c&128)==0) != (old_uppercase==0) )
  {
    if(typewriter_nchars<TYPEWRITER_TABLE_SIZE)
    {
      typewriter_char_table[typewriter_nchars] = ((c&128)==0)?58:60;
      typewriter_SY(typewriter_char_table[typewriter_nchars]);
      typewriter_nchars++;
    }
    else
      fprintf(stderr, "No room for case char.\n");
  }
  if(typewriter_nchars<TYPEWRITER_TABLE_SIZE)
  {
    typewriter_char_table[typewriter_nchars] = c&127;
    typewriter_SY(typewriter_char_table[typewriter_nchars]);
    typewriter_nchars++;
  }
  else
    fprintf(stderr, "No room char.\n");
}

static int uclc=0;

static void typewriter_input_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  XmDrawingAreaCallbackStruct *cbstruct = (XmDrawingAreaCallbackStruct *) call_data;
  XEvent *event = cbstruct -> event;
  int i,ilen,keycode;
  unsigned char ch2;

  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "typewriter_input_callback called.\n");
    fprintf(debug_fh, "event type: %d\n", event->type);
  }

  if(event->type == KeyPress)
  {
    if(debug&DEBUGinterface) fprintf(debug_fh, "state: %d, keycode: %d\n", event->xkey.state, event->xkey.keycode);
    if(typewriter_waiting)
    {
      if(typewriter_nchars<(TYPEWRITER_TABLE_SIZE-1))
      {
	ilen=XLookupString((XKeyEvent *) event, strbuf, sizeof(strbuf), (KeySym *) &keycode, &compose);
	if(debug&DEBUGinterface) fprintf(debug_fh, "ilen: %d, keycode: %4.4x\n", ilen, keycode);
	if(keycode==XK_Shift_L || keycode==XK_Shift_R)
	{
	  uclc=1;
#ifdef TYPEWRITER_SOUND
	  if(sound_enabled) pa_simple_write(pa_s, typewriter_sound_uc, typewriter_sound_uc_len, &pa_error);
#endif
	}
	for(i=0; i<ilen; i++)
	{
	  if(debug&DEBUGinterface) fprintf(debug_fh, "  character: %c\n", strbuf[i]);
	  uclc = 0;

	  old_uppercase = uppercase;
	  
	  ch2 = a2flx(strbuf[i], &uppercase);

	  if(ch2 == 255)
	  {
	    if(debug&DEBUGinterface) fprintf(debug_fh, "  Unknown character: %c\n", strbuf[i]);
	  }
	  else
	  {
	    savechar(ch2+128*uppercase);
	    YE_wait=0;
	  }
	}
      }
      else
      {
	if(debug&DEBUGinterface) fprintf(debug_fh, "Character discarded; no room in typewriter_char_table\n");
      }
    }
    else
    {
      if(debug&DEBUGinterface) fprintf(debug_fh, "Character discarded; not waiting for input\n");
    }
  }
  else if(event->type == KeyRelease)
  {
    if(debug&DEBUGinterface) fprintf(debug_fh, "state: %d, keycode: %d\n", event->xkey.state, event->xkey.keycode);
    if(typewriter_waiting)
    {
      if(typewriter_nchars<(TYPEWRITER_TABLE_SIZE-2))
      {
	ilen=XLookupString((XKeyEvent *) event, strbuf, sizeof(strbuf), (KeySym *) &keycode, &compose);
	if(debug&DEBUGinterface) fprintf(debug_fh, "ilen: %d, keycode: %4.4x\n", ilen, keycode);
	if(keycode==XK_Shift_L || keycode==XK_Shift_R)
	{
#ifdef TYPEWRITER_SOUND
	  if(sound_enabled) pa_simple_write(pa_s, typewriter_sound_lc, typewriter_sound_lc_len, &pa_error);
#endif
	  if(uclc)
	  {
	    typewriter_char_table[typewriter_nchars++] = 60;
	    typewriter_char_table[typewriter_nchars++] = 58;
	    if(debug&DEBUGinterface) fprintf(debug_fh, "Generate UC/LC\n");
	    YE_wait=0;
	    uclc=0;
	  }
	}
      }
    }
  }
}

static void typewriter_destroy_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  typewriter_wnd = NULL;

  update_viewmenu();
}

void typewriter_lowercase()
{
  /* Called by Reset/Mikrotempi stop */
  uppercase=0;
  typewriter_nchars=0;
  compose.compose_ptr = NULL;
  compose.chars_matched = FALSE;
}

void typewriter_init()
{
  int st;
  int i,j,k;
  double x;
  XGCValues values;
  XtGCMask valueMask;
  XmString stmp;
  Position dx,dy;
  int value_return, slider_size_return, increment_return, page_increment_return;
  int slider_max;

  if(typewriter_wnd != NULL)
  {
  }
  else
  {
    /* Get resources */

    XtGetApplicationResources(toplevel, &typewriter_applicationdata,
      typewriter_resources, XtNumber(typewriter_resources), NULL, 0);

    /*
	Font administration:
    */

    typewriter_font_width = typewriter_applicationdata.textfont->max_bounds.width;
    typewriter_font_height = typewriter_applicationdata.textfont->ascent+typewriter_applicationdata.textfont->descent;
    typewriter_drawing_width=typewriter_font_width*PAPERWIDTH;
    typewriter_drawing_height=typewriter_font_height*LINEBUFFER;

    n=0;
    XtSetArg(wargs[n], XtNtitle, "GIER Typewriter"); n++;
#ifdef GIANTTYPEWRITER
    XtSetArg(wargs[n], XtNgeometry, "+10+10"); n++;
#else
    XtSetArg(wargs[n], XtNgeometry, "+10-100"); n++;
#endif
    typewriter_wnd = XtAppCreateShell("typewriter", "typewriter", topLevelShellWidgetClass, XtDisplay(toplevel), wargs, n);
    XtAddCallback(typewriter_wnd, XtNdestroyCallback, typewriter_destroy_callback, NULL);

    n=0;
    typewriter_form=XtCreateManagedWidget("typewriter_form", xmFormWidgetClass, typewriter_wnd, wargs, n);

    n=0;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNrightAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNforeground, typewriter_applicationdata.lampcolor); n++;
    XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple("     "))); n++;
    typewriter_input_label=XtCreateManagedWidget("typewriter_input_label", xmLabelWidgetClass, typewriter_form, wargs, n);
    XmStringFree(stmp);

    n=0;
    XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
    XtSetArg(wargs[n], XmNtopWidget, typewriter_input_label); n++;
    XtSetArg(wargs[n], XmNrightAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNbottomAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNscrollBarPlacement, XmBOTTOM_LEFT); n++;
    XtSetArg(wargs[n], XmNscrollingPolicy, XmAUTOMATIC); n++;
    XtSetArg(wargs[n], XmNwidth, typewriter_drawing_width+32); n++; /* Kludge */
#ifdef GIANTTYPEWRITER
    XtSetArg(wargs[n], XmNheight, 850); n++; /* Kludge */
#else
    XtSetArg(wargs[n], XmNheight, 250); n++; /* Kludge */
#endif
    typewriter_swindow=XtCreateManagedWidget("typewriter_swindow", xmScrolledWindowWidgetClass, typewriter_form, wargs, n);

    n=0;
    XtSetArg(wargs[n], XmNhorizontalScrollBar, &typewriter_horScroll); n++;
    XtSetArg(wargs[n], XmNverticalScrollBar, &typewriter_verScroll); n++;
    XtGetValues(typewriter_swindow, wargs, n);

    XtUnmanageChild(typewriter_horScroll);

    n=0;
    XtSetArg(wargs[n], XmNwidth, typewriter_drawing_width); n++;
    XtSetArg(wargs[n], XmNheight, typewriter_drawing_height); n++;
    XtSetArg(wargs[n], XmNbackground, WhitePixel(XtDisplay(typewriter_swindow),XDefaultScreen(XtDisplay(typewriter_swindow)))); n++;
    typewriter_drawing=XtCreateManagedWidget("typewriter_drawing", xmDrawingAreaWidgetClass, typewriter_swindow, wargs, n);
    XtAddCallback(typewriter_drawing, XmNinputCallback, typewriter_input_callback, (XtPointer) NULL);
    XtAddCallback(typewriter_drawing, XmNexposeCallback, typewriter_expose1_callback, (XtPointer) NULL);
    XtAddEventHandler(typewriter_drawing, (EventMask) 0, TRUE, typewriter_expose2_callback, (XtPointer) NULL);

    /*
	Create GC's:
    */

    valueMask = GCForeground | GCBackground | GCFont;
    values.foreground = typewriter_applicationdata.blackcolor;
    values.background = WhitePixel(XtDisplay(typewriter_drawing),XDefaultScreen(XtDisplay(typewriter_drawing)));
    values.font=typewriter_applicationdata.textfont->fid;
    black_normal_gc = XtGetGC(typewriter_drawing, valueMask, &values);

    valueMask = GCForeground | GCBackground | GCFont;
    values.foreground = typewriter_applicationdata.redcolor;
    values.background = WhitePixel(XtDisplay(typewriter_drawing),XDefaultScreen(XtDisplay(typewriter_drawing)));
    values.font=typewriter_applicationdata.textfont->fid;
    red_normal_gc = XtGetGC(typewriter_drawing, valueMask, &values);

    valueMask = GCForeground | GCBackground | GCFont;
    values.foreground = typewriter_applicationdata.blackcolor;
    values.background = WhitePixel(XtDisplay(typewriter_drawing),XDefaultScreen(XtDisplay(typewriter_drawing)));
    values.font=typewriter_applicationdata.symbolfont->fid;
    black_symbol_gc = XtGetGC(typewriter_drawing, valueMask, &values);

    valueMask = GCForeground | GCBackground | GCFont;
    values.foreground = typewriter_applicationdata.redcolor;
    values.background = WhitePixel(XtDisplay(typewriter_drawing),XDefaultScreen(XtDisplay(typewriter_drawing)));
    values.font=typewriter_applicationdata.symbolfont->fid;
    red_symbol_gc = XtGetGC(typewriter_drawing, valueMask, &values);

    valueMask = GCForeground | GCBackground | GCFont;
    values.foreground = typewriter_applicationdata.blackcolor;
    values.background = WhitePixel(XtDisplay(typewriter_drawing),XDefaultScreen(XtDisplay(typewriter_drawing)));
    values.font=typewriter_applicationdata.subscriptfont->fid;
    black_subscript_gc = XtGetGC(typewriter_drawing, valueMask, &values);

    valueMask = GCForeground | GCBackground | GCFont;
    values.foreground = typewriter_applicationdata.redcolor;
    values.background = WhitePixel(XtDisplay(typewriter_drawing),XDefaultScreen(XtDisplay(typewriter_drawing)));
    values.font=typewriter_applicationdata.subscriptfont->fid;
    red_subscript_gc = XtGetGC(typewriter_drawing, valueMask, &values);

    gc_table[0][0] = black_normal_gc;
    gc_table[1][0] = red_normal_gc;
    gc_table[0][1] = black_symbol_gc;
    gc_table[1][1] = red_symbol_gc;
    gc_table[0][2] = black_subscript_gc;
    gc_table[1][2] = red_subscript_gc;

    valueMask = GCBackground | GCGraphicsExposures;
    values.background = WhitePixel(XtDisplay(typewriter_drawing),XDefaultScreen(XtDisplay(typewriter_drawing)));
    values.graphics_exposures = (Bool) True;
    scroll_gc = XtGetGC(typewriter_drawing, valueMask, &values);

    XtRealizeWidget(typewriter_wnd);

    /* Scroll to bottom */

    n=0;
    XtSetArg(wargs[n], XmNmaximum, &slider_max); n++;
    XtGetValues(typewriter_verScroll, wargs, n);
    XmScrollBarGetValues(typewriter_verScroll, &value_return, &slider_size_return, &increment_return, &page_increment_return);
    value_return = slider_max-slider_size_return;
    XmScrollBarSetValues(typewriter_verScroll, value_return, slider_size_return, increment_return, page_increment_return, True);

    uppercase=0;
    cur_row=LINEBUFFER-1;
    cur_col=0;
    for(i=0; i<LINEBUFFER; i++)
    for(j=0; j<PAPERWIDTH; j++)
    for(k=0; k<MAXOVERWRITE; k++)
    paper[i][j][k] = 0;

    compose.compose_ptr = NULL;
    compose.chars_matched = FALSE;

#ifdef TYPEWRITER_SOUND
    if(pa_s != NULL)
    {
      pa_simple_free(pa_s);
      pa_s = NULL;
    }

    pa_ss.format = PA_SAMPLE_S16LE;
    pa_ss.channels = 1;
    pa_ss.rate = 44100;

    pa_s = pa_simple_new(NULL, "GIER Typewriter", PA_STREAM_PLAYBACK, NULL,
			 "GIER Typewriter", &pa_ss, NULL, NULL, &pa_error);

    if(!pa_s)
    {
      fprintf(stderr, "typewriter sound, pa_simple_new failed: %s\n", pa_strerror(pa_error));
    }
#endif
  }
}

void typewriter_destroy()
{
  XtUnrealizeWidget(typewriter_wnd);
  XtDestroyWidget(typewriter_wnd);
#ifdef TYPEWRITER_SOUND
  if(pa_s != NULL)
  {
    pa_simple_free(pa_s);
    pa_s = NULL;
  }
#endif
}

int typewriter_is_visible()
{
  return(typewriter_wnd!=NULL);
}

typedef struct
{
  XFontStruct *textfont;
  Pixel tapebackground;
} reader_ApplicationData, *reader_ApplicationDataPtr;

static reader_ApplicationData reader_applicationdata;

static XtResource reader_resources[] =
{
  {"textfont", "Textfont", XtRFontStruct, sizeof(XFontStruct *),
   XtOffset(reader_ApplicationDataPtr, textfont),
#ifdef GIANTTYPEWRITER
   XtRString, "-adobe-courier-bold-r-*-*-*-180-100-100-*-*-*-*"
#else
   XtRString, "-adobe-courier-medium-r-*-*-*-180-75-75-*-*-*-*"
#endif
  },
  {"tapebackground", "Tapebackground", XtRPixel, sizeof(Pixel),
   XtOffset(reader_ApplicationDataPtr, tapebackground),
   XtRString, "gray"
  },
};

void reader_update()
{
  int i,j,k;
  char txt[READER_TAPE_WINDOW+READER_TAPE_OFFSET+1];
  unsigned char c;
  int mask;
  int reader_uppercase;

  if(reader_wnd == NULL) return;

  if(!reader_modified) return;

  txt[READER_TAPE_WINDOW+READER_TAPE_OFFSET] = '\0';

  if(debug&DEBUGinterface) fprintf(debug_fh, "reader_update: reader_nchars: %d\n", reader_nchars);

  for(j=0; j<READER_TAPE_WINDOW+READER_TAPE_OFFSET; j++)
  {
    if((j-READER_TAPE_OFFSET)>=reader_nchars)
    {
      c = ' ';
    }
    else
    {
      reader_uppercase = 0;
      c = flx2a(remove_parity((unsigned char) reader_char_table[j]), &reader_uppercase);
      if(c==255) c=' ';
    }
    txt[j] = c;
  }
  XDrawImageString(XtDisplay(reader_drawing), XtWindow(reader_drawing), reader_gray_gc, 0, reader_CursorY(0), txt, READER_TAPE_WINDOW+READER_TAPE_OFFSET);

  mask = 1;
  for(i=0; i<9; i++)
  {
    if(i==3)
    {
      for(j=0; j<READER_TAPE_WINDOW+READER_TAPE_OFFSET; j++) txt[j]='.';
    }
    else
    {
      for(j=0; j<READER_TAPE_WINDOW+READER_TAPE_OFFSET; j++)
      {
	if((j-READER_TAPE_OFFSET)>=reader_nchars)
	  k=0;
	else
	  k=reader_char_table[j];
	txt[j]= (k&mask)?'o':' ';
      }
      mask=mask<<1;
    }
    XDrawImageString(XtDisplay(reader_drawing), XtWindow(reader_drawing), reader_hole_gc, 0, reader_CursorY(9-i), txt, READER_TAPE_WINDOW+READER_TAPE_OFFSET);
    if(debug&DEBUGinterface) fprintf(debug_fh, "Print: %s\n", txt);
  }

  for(j=0; j<READER_TAPE_WINDOW+READER_TAPE_OFFSET; j++) txt[j]=' ';
  txt[READER_TAPE_OFFSET] = '^';
  XDrawImageString(XtDisplay(reader_drawing), XtWindow(reader_drawing), reader_gray_gc, 0, reader_CursorY(10), txt, READER_TAPE_WINDOW+READER_TAPE_OFFSET);

  reader_modified = False;
}

static void reader_update_buffer()
{
  int nread;
  unsigned char c1,c2;
  int i,old_uppercase;

  reader_modified = True;

  if(reader_nchars == READER_TAPE_WINDOW+READER_TAPE_OFFSET) return;
  if(reader_current_fd == -1) return;

  if(reader_filetype == 0)
  {
    while(reader_nchars < READER_TAPE_WINDOW+READER_TAPE_OFFSET && reader_current_fd != -1)
    {
      nread=read(reader_current_fd, &c1, 1);

      if(nread <= 0)
      {
	close(reader_current_fd);
	reader_current_fd = -1;
	nread=0;
      }
      else if(c1 != 0)
      {
	/* Skip blank paper tape */
	reader_char_table[reader_nchars++] = c1;
      }
    }
  }
  else
  {
    /* read&convert ASCII tape */
    while(reader_nchars<(READER_TAPE_WINDOW+READER_TAPE_OFFSET))
    {
      if(reader_nsavedchars>0)
      {
	c2 = reader_savedchars[--reader_nsavedchars];
	nread=1;
      }
      else
      {
	nread=read(reader_current_fd, (char *) &c1, 1);
	if(nread <= 0)
	{
	  close(reader_current_fd);
	  reader_current_fd = -1;
	  nread=0;
	  goto ENDREAD;
	}
	else
	{
	  if(c1 == '\t')
	  {
	    do
	    {
	      reader_savedchars[reader_nsavedchars++] = 0;
	      reader_linepos++;
	    } while(reader_linepos%8!=0);
	    c2 = reader_savedchars[--reader_nsavedchars];
	    nread=1;
	  }
	  else
	  {
	    if(c1 == '#')
	    {
	      c2=0;
	      for(i=0; i<3; i++)
	      {
		nread=read(reader_current_fd, (char *) &c1, 1);
		if(nread <= 0)
		{
		  close(reader_current_fd);
		  reader_current_fd = -1;
		  nread=0;
		  goto ENDREAD;
		}
		if(c1>='0' && c1<='9') c2=c2*10+c1-'0';
	      }
	      c2 = c2&127;
	    }
	    else
	    {
	      old_uppercase = reader_uppercase;
	      c2 = a2flx(c1, &reader_uppercase);

	      if(old_uppercase != reader_uppercase)
	      {
		reader_savedchars[reader_nsavedchars++] = c2;
		c2 = reader_uppercase?60:58;
	      } /* if case shift */
	    } /* not #ddd */
	  } /* not TAB */
	} /* not end of file */
      } /* no chars saved */
      if(nread>0)
      {
	if(c2==64)
	{
	  reader_linepos=0;
	}
	else
	{
	  reader_linepos++;
	}
	reader_char_table[reader_nchars] = set_parity(c2);
      }
      reader_nchars += nread;
    } /* fill buffer */
  } /* ASCII read */

ENDREAD: ;

}

void reader_wait(int status)
{
  if(status)
  {
    YE_wait=1;
  }
  else
  {
    YE_wait=0;
    reader_update_buffer();
  }
}

static void reader_fileopen_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  XmFileSelectionBoxCallbackStruct *cbs = (XmFileSelectionBoxCallbackStruct *)call_data;
  char *filename;

  if(reader_current_fd != -1) close(reader_current_fd);

  XmStringGetLtoR(cbs->value, XmSTRING_DEFAULT_CHARSET, &filename);
  XtUnmanageChild(w);

  if(debug&DEBUGinterface) fprintf(debug_fh, "Opening file: %s\n", filename);

  reader_current_fd = open(filename, O_RDONLY);

  reader_uppercase=0;

  if(!strncmp(filename+strlen(filename)-3,"flx",3))
    reader_filetype=0;
  else
    reader_filetype=1;

  if(debug&DEBUGinterface) fprintf(debug_fh, "reader_filetype: %d\n", reader_filetype);

  reader_update_buffer();
  reader_modified = True;
  reader_update();
  if(reader_nchars>0) YE_wait=0;

  XtFree(filename);
}

static void reader_fileclose_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  XtUnmanageChild(w);
}

static void pread_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  XmFileSelectionDoSearch(reader_filedialog, NULL);
  XtManageChild(reader_filedialog);
}

static void reset_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  int i;
  reader_nchars=READER_TAPE_OFFSET;

  for(i=0; i<READER_TAPE_OFFSET; i++) reader_char_table[i]=0;

  pread_callback(w, client_data, call_data);
}

static void skip_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
}

static void up_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
}

static void reader_expose_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  XmDrawingAreaCallbackStruct *cbstruct = (XmDrawingAreaCallbackStruct *) call_data;
  XEvent *event=cbstruct->event;
  int row1,row2;

  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "reader_expose_callback called. Event type: %d\n", event->type);
    fprintf(debug_fh, "x: %d, y: %d\n", event->xexpose.x, event->xexpose.y);
    fprintf(debug_fh, "width: %d, height: %d\n", event->xexpose.width, event->xexpose.height);
  }

  reader_modified = True;
  reader_update();
}

static void reader_input_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  XmDrawingAreaCallbackStruct *cbstruct = (XmDrawingAreaCallbackStruct *) call_data;
  XEvent *event = cbstruct -> event;
  int i,ilen,keycode;

  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "reader_input_callback called.\n");
    fprintf(debug_fh, "event type: %d\n", event->type);
  }

}

static void reader_destroy_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  reader_wnd = NULL;

  update_viewmenu();
}

void reader_init()
{
  int st;
  int i,j,k;
  double x;
  XGCValues values;
  XtGCMask valueMask;
  XmString stmp,stmp2;
  Position dx,dy;

  if(reader_wnd != NULL)
  {
  }
  else
  {
    /* Get resources */

    XtGetApplicationResources(toplevel, &reader_applicationdata,
      reader_resources, XtNumber(reader_resources), NULL, 0);

    /*
	Font administration:
    */

    reader_font_width = reader_applicationdata.textfont->max_bounds.width;
    reader_font_height = reader_applicationdata.textfont->ascent+reader_applicationdata.textfont->descent;
    reader_drawing_width=reader_font_width*(READER_TAPE_WINDOW+READER_TAPE_OFFSET);
    reader_drawing_height=reader_font_height*11;

    n=0;
    XtSetArg(wargs[n], XtNtitle, "GIER Paper Tape Reader"); n++;
    XtSetArg(wargs[n], XtNgeometry, "+10+110"); n++;
    reader_wnd = XtAppCreateShell("reader", "reader", topLevelShellWidgetClass, XtDisplay(toplevel), wargs, n);
    XtAddCallback(reader_wnd, XtNdestroyCallback, reader_destroy_callback, NULL);

    n=0;
    reader_form=XtCreateManagedWidget("reader_form", xmFormWidgetClass, reader_wnd, wargs, n);

    n=0;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple("RESET"))); n++;
    reader_reset = XtCreateManagedWidget("reader_reset", xmPushButtonWidgetClass, reader_form, wargs, n);
    XtAddCallback(reader_reset, XmNactivateCallback, reset_callback, NULL);
    XmStringFree(stmp);

    n=0;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
    XtSetArg(wargs[n], XmNleftWidget, reader_reset); n++;
    XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple("READ"))); n++;
    reader_pread = XtCreateManagedWidget("reader_pread", xmPushButtonWidgetClass, reader_form, wargs, n);
    XtAddCallback(reader_pread, XmNactivateCallback, pread_callback, NULL);
    XmStringFree(stmp);

    n=0;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
    XtSetArg(wargs[n], XmNleftWidget, reader_pread); n++;
    XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple("SKIP"))); n++;
    reader_skip = XtCreateManagedWidget("reader_skip", xmPushButtonWidgetClass, reader_form, wargs, n);
    XtAddCallback(reader_skip, XmNactivateCallback, skip_callback, NULL);
    XmStringFree(stmp);

    n=0;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
    XtSetArg(wargs[n], XmNleftWidget, reader_skip); n++;
    XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple("UP"))); n++;
    reader_up = XtCreateManagedWidget("reader_up", xmPushButtonWidgetClass, reader_form, wargs, n);
    XtAddCallback(reader_up, XmNactivateCallback, up_callback, NULL);
    XmStringFree(stmp);

    n=0;
    XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
    XtSetArg(wargs[n], XmNtopWidget, reader_reset); n++;
    XtSetArg(wargs[n], XmNrightAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNbottomAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNwidth, reader_drawing_width); n++;
    XtSetArg(wargs[n], XmNheight, reader_drawing_height); n++;
    XtSetArg(wargs[n], XmNbackground, reader_applicationdata.tapebackground); n++;
    reader_drawing=XtCreateManagedWidget("reader_drawing", xmDrawingAreaWidgetClass, reader_form, wargs, n);
    XtAddCallback(reader_drawing, XmNexposeCallback, reader_expose_callback, (XtPointer) NULL);
    XtAddCallback(reader_drawing, XmNinputCallback, reader_input_callback, (XtPointer) NULL);

    n=0;
    XtSetArg(wargs[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++;
    XtSetArg(wargs[n], XmNautoUnmanage, False); n++;
    XtSetArg(wargs[n], XmNnoResize, TRUE); n++;
    XtSetArg(wargs[n], XmNresizePolicy, XmRESIZE_NONE); n++;
    XtSetArg(wargs[n], XmNpattern, (stmp=XmStringCreateSimple("*"))); n++;
    XtSetArg(wargs[n], XmNdialogTitle, (stmp2=XmStringCreateSimple("Reader File Selection"))); n++;

    reader_filedialog = XmCreateFileSelectionDialog(reader_form, "FileDialog", wargs, n);
    XmStringFree(stmp);
    XmStringFree(stmp2);

    XtAddCallback(reader_filedialog, XmNokCallback, reader_fileopen_callback, (XtPointer)NULL);
    XtAddCallback(reader_filedialog, XmNcancelCallback, reader_fileclose_callback, (XtPointer)NULL);

    /*
	Create GC's:

	The paper is white, holes are black, and the background is gray:
    */

    valueMask = GCForeground | GCBackground | GCFont;
    values.foreground = BlackPixel(XtDisplay(reader_drawing),XDefaultScreen(XtDisplay(reader_drawing)));
    values.background = WhitePixel(XtDisplay(reader_drawing),XDefaultScreen(XtDisplay(reader_drawing)));
    values.font=reader_applicationdata.textfont->fid;
    reader_hole_gc = XtGetGC(reader_drawing, valueMask, &values);

    valueMask = GCForeground | GCBackground | GCFont;
    values.foreground = BlackPixel(XtDisplay(reader_drawing),XDefaultScreen(XtDisplay(reader_drawing)));
    values.background = reader_applicationdata.tapebackground;
    values.font=reader_applicationdata.textfont->fid;
    reader_gray_gc = XtGetGC(reader_drawing, valueMask, &values);

    XtRealizeWidget(reader_wnd);

  }
}

void reader_destroy()
{
  XtUnrealizeWidget(reader_wnd);
  XtDestroyWidget(reader_wnd);
}

int reader_is_visible()
{
  return(reader_wnd!=NULL);
}


static void printer_setfilename(char *filename)
{
  XmString stmp;

  n=0;
  XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple(filename))); n++;
  XtSetValues(printer_psfilename, wargs, n);

  XmStringFree(stmp);
}

static void printer_update()
{
}

static void printer_papersize_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  char *s;

  s = XmTextFieldGetString(lines_edit);
  sscanf(s, "%d", &lines);
  XFree(s);

  letter = (long) client_data;
  if(letter && (lines==67))
  {
    lines = 63;
  }
  else if((!letter) && (lines==63))
  {
    lines = 67;
  }
  sprintf(lines_buffer, "%d", lines);
  XmTextFieldSetString(lines_edit, lines_buffer);
}

static void printer_filedialog_create()
{
  XmString stmp;
  Widget papersize_label, papersize_pane,
	 lpi_edit_label, fontsize_edit_label, lines_edit_label, uadvance_select_label;
  Widget A4_w, Letter_w;

  n=0;
  XtSetArg(wargs[n], XtNtitle, "PostScript File Selection"); n++;
  printer_filedialog = XtAppCreateShell("printer_filedialog", "printer_filedialog", topLevelShellWidgetClass, XtDisplay(toplevel), wargs, n);

  n=0;
  printer_fileform = XtCreateManagedWidget("form", xmFormWidgetClass, printer_filedialog, wargs, n);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNpattern, (stmp=XmStringCreateSimple("*.ps"))); n++;
  printer_fileselection = XtCreateManagedWidget("printer_fileselection", xmFileSelectionBoxWidgetClass, printer_fileform, wargs, n);
  XmStringFree(stmp);

  XtAddCallback(printer_fileselection, XmNokCallback, printer_fileopen_callback, (XtPointer)NULL);
  XtAddCallback(printer_fileselection, XmNcancelCallback, printer_fileclose_callback, (XtPointer)NULL);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNtopOffset, 5); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNleftWidget, printer_fileselection); n++;
  XtSetArg(wargs[n], XmNleftOffset, 10); n++;
  papersize_label = XtCreateManagedWidget("Select paper size", xmLabelWidgetClass, printer_fileform, wargs, n);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, papersize_label); n++;
  XtSetArg(wargs[n], XmNleftOffset, 0); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNleftWidget, printer_fileselection); n++;
  papersize = XmCreateOptionMenu(printer_fileform, "papersize", wargs, n);

  n=0;
  papersize_pane = XmCreatePulldownMenu(papersize, "pulldown", wargs, n);

  n=0;
  XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateLocalized("A4"))); n++;
  A4_w = XtCreateManagedWidget("A4_select", xmPushButtonWidgetClass, papersize_pane, wargs, n);
  XtAddCallback(A4_w, XmNactivateCallback, printer_papersize_callback, (XtPointer) 0);

  n=0;
  XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateLocalized("Letter"))); n++;
  Letter_w = XtCreateManagedWidget("letter_select", xmPushButtonWidgetClass, papersize_pane, wargs, n);
  XtAddCallback(Letter_w, XmNactivateCallback, printer_papersize_callback, (XtPointer) 1);

  n=0;
  XtSetArg(wargs[n], XmNsubMenuId, papersize_pane); n++;
  XtSetValues(papersize, wargs, n);

  XtManageChild(papersize);

  n=0;
  XtSetArg(wargs[n], XmNmenuHistory, letter?Letter_w:A4_w); n++;
  XtSetValues(papersize, wargs, n);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, papersize); n++;
  XtSetArg(wargs[n], XmNtopOffset, 5); n++;
  XtSetArg(wargs[n], XmNleftOffset, 10); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNleftWidget, printer_fileselection); n++;
  lpi_edit_label = XtCreateManagedWidget("Select lines per inch", xmLabelWidgetClass, printer_fileform, wargs, n);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, lpi_edit_label); n++;
  XtSetArg(wargs[n], XmNleftOffset, 10); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNleftWidget, printer_fileselection); n++;
  XtSetArg(wargs[n], XmNvalue, lpi_buffer); n++;
  sprintf(lpi_buffer, "%d", lpi);
  XtSetArg(wargs[n], XmNmaxLength, sizeof(lpi_buffer)-1); n++;
  XtSetArg(wargs[n], XmNcolumns, sizeof(lpi_buffer)-1); n++;
  XtSetArg(wargs[n], XmNverifyBell, False); n++;

  lpi_edit = XtCreateManagedWidget("lpi_edit", xmTextFieldWidgetClass, printer_fileform, wargs, n);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, lpi_edit); n++;
  XtSetArg(wargs[n], XmNtopOffset, 5); n++;
  XtSetArg(wargs[n], XmNleftOffset, 10); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNleftWidget, printer_fileselection); n++;
  fontsize_edit_label = XtCreateManagedWidget("Select font size", xmLabelWidgetClass, printer_fileform, wargs, n);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, fontsize_edit_label); n++;
  XtSetArg(wargs[n], XmNleftOffset, 10); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNleftWidget, printer_fileselection); n++;
  XtSetArg(wargs[n], XmNvalue, fontsize_buffer); n++;
  sprintf(fontsize_buffer, "%lg", fontsize);
  XtSetArg(wargs[n], XmNmaxLength, sizeof(fontsize_buffer)-1); n++;
  XtSetArg(wargs[n], XmNcolumns, sizeof(fontsize_buffer)-1); n++;
  XtSetArg(wargs[n], XmNverifyBell, False); n++;

  fontsize_edit = XtCreateManagedWidget("fontsize_edit", xmTextFieldWidgetClass, printer_fileform, wargs, n);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, fontsize_edit); n++;
  XtSetArg(wargs[n], XmNtopOffset, 5); n++;
  XtSetArg(wargs[n], XmNleftOffset, 10); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNleftWidget, printer_fileselection); n++;
  lines_edit_label = XtCreateManagedWidget("Select lines per page", xmLabelWidgetClass, printer_fileform, wargs, n);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, lines_edit_label); n++;
  XtSetArg(wargs[n], XmNleftOffset, 10); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNleftWidget, printer_fileselection); n++;
  XtSetArg(wargs[n], XmNvalue, lines_buffer); n++;
  sprintf(lines_buffer, "%d", lines);
  XtSetArg(wargs[n], XmNmaxLength, sizeof(lines_buffer)-1); n++;
  XtSetArg(wargs[n], XmNcolumns, sizeof(lines_buffer)-1); n++;
  XtSetArg(wargs[n], XmNverifyBell, False); n++;

  lines_edit = XtCreateManagedWidget("lines_edit", xmTextFieldWidgetClass, printer_fileform, wargs, n);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, lines_edit); n++;
  XtSetArg(wargs[n], XmNtopOffset, 5); n++;
  XtSetArg(wargs[n], XmNleftOffset, 10); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNleftWidget, printer_fileselection); n++;
  uadvance_select_label = XtCreateManagedWidget("Print _ and | correctly", xmLabelWidgetClass, printer_fileform, wargs, n);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, uadvance_select_label); n++;
  XtSetArg(wargs[n], XmNleftOffset, 10); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNleftWidget, printer_fileselection); n++;
  uadvance_check = XmCreateSimpleCheckBox(printer_fileform, "uadvance_check", wargs, n);
  XtManageChild(uadvance_check);

  n=0;
#if XmVERSION==1
  XtSetArg(wargs[n], XmNindicatorOn, True); n++;
  XtSetArg(wargs[n], XmNset, uadvance?True:False); n++;
#else
  XtSetArg(wargs[n], XmNindicatorOn, XmINDICATOR_CHECK_BOX); n++;
  XtSetArg(wargs[n], XmNset, uadvance?XmSET:XmUNSET); n++;
#endif
  XtSetArg(wargs[n], XmNindicatorSize, 20); n++;
  uadvance_select = XmCreateToggleButton(uadvance_check, "", wargs, n);
  XtManageChild(uadvance_select);


  XtRealizeWidget(printer_filedialog);

  XmFileSelectionDoSearch(printer_fileselection, NULL);
}

static void printer_filedialog_close()
{
  XtDestroyWidget(printer_filedialog);
}

void printer_SY(char c)
{
  while(!is_printing)
  {
    if(printer_wnd == NULL) printer_init();
    printer_filedialog_create();
    while((!is_printing) || XtAppPending(app))
    {
      XtAppProcessEvent(app,XtIMAll);
    }
  }

  ps_SY((unsigned char) c);

  printer_update();
}

static void printer_fileopen_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  XmFileSelectionBoxCallbackStruct *cbs = (XmFileSelectionBoxCallbackStruct *)call_data;
  char *filename;
  char *s;

  if(is_printing)
  {
    ps_close();
    is_printing=0;
  }

  XmStringGetLtoR(cbs->value, XmSTRING_DEFAULT_CHARSET, &filename);

  /* Get parameters */

  s = XmTextFieldGetString(lpi_edit);
  sscanf(s, "%d", &lpi);
  XFree(s);

  s = XmTextFieldGetString(fontsize_edit);
  sscanf(s, "%lg", &fontsize);
  XFree(s);

  s = XmTextFieldGetString(lines_edit);
  sscanf(s, "%d", &lines);
  XFree(s);

  n=0;
  uadvance=0;
  XtSetArg(wargs[n], XmNset, &uadvance); n++;
  XtGetValues(uadvance_select, wargs, n);

  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "lpi: %d\n", lpi);
    fprintf(debug_fh, "fontsize: %lg\n", fontsize);
    fprintf(debug_fh, "lines: %d\n", lines);
    fprintf(debug_fh, "uadvance: %d\n", uadvance);
    fprintf(debug_fh, "Opening file: %s\n", filename);
  }

  printer_filedialog_close();

  is_printing = ps_open(filename, letter, fontsize, lpi, lines, !uadvance);

  if(is_printing) printer_setfilename(filename);
  XtFree(filename);
}

static void printer_fileclose_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  printer_filedialog_close();
}

static void printer_endfile_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  int i;

  if(is_printing)
  {
    ps_close();
    is_printing = 0;
    printer_setfilename("- no file -");
  }
  printer_update();
}

static void printer_newfile_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  printer_endfile_callback(w, client_data, call_data);
  printer_filedialog_create();
}

static void printer_destroy_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  printer_wnd = NULL;

  update_viewmenu();
}


void printer_init()
{
  int st;
  int i,j,k;
  double x;
  XmString stmp,stmp2;

  if(printer_wnd != NULL)
  {
  }
  else
  {
    n=0;
    XtSetArg(wargs[n], XtNtitle, "GIER Lineprinter"); n++;
    XtSetArg(wargs[n], XtNgeometry, "+10+375"); n++;
    printer_wnd = XtAppCreateShell("printer", "printer", topLevelShellWidgetClass, XtDisplay(toplevel), wargs, n);
    XtAddCallback(printer_wnd, XtNdestroyCallback, printer_destroy_callback, NULL);

    n=0;
    printer_form=XtCreateManagedWidget("printer_form", xmFormWidgetClass, printer_wnd, wargs, n);

    n=0;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple("New File"))); n++;
    printer_newfile = XtCreateManagedWidget("printer_newfile", xmPushButtonWidgetClass, printer_form, wargs, n);
    XtAddCallback(printer_newfile, XmNactivateCallback, printer_newfile_callback, NULL);
    XmStringFree(stmp);

    n=0;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
    XtSetArg(wargs[n], XmNleftWidget, printer_newfile); n++;
    XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple("End File"))); n++;
    printer_endfile = XtCreateManagedWidget("printer_endfile", xmPushButtonWidgetClass, printer_form, wargs, n);
    XtAddCallback(printer_endfile, XmNactivateCallback, printer_endfile_callback, NULL);
    XmStringFree(stmp);

    n=0;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
    XtSetArg(wargs[n], XmNtopWidget, printer_newfile); n++;
    XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNrightAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple("- no file -"))); n++;
    printer_psfilename = XtCreateManagedWidget("printer_psfilename", xmLabelWidgetClass, printer_form, wargs, n);
    XmStringFree(stmp);

    XtRealizeWidget(printer_wnd);

  }
}

void printer_destroy()
{
  XtUnrealizeWidget(printer_wnd);
  XtDestroyWidget(printer_wnd);
}

int printer_is_visible()
{
  return(printer_wnd!=NULL);
}

/* Plotter */

static void plotter_close()
{
}

static void plotout_setfilename(char *filename)
{
  XmString stmp;

  n=0;
  XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple(filename))); n++;
  XtSetValues(plotout_psfilename, wargs, n);

  XmStringFree(stmp);
}

static void plotter_update()
{
  XmString stmp;
  char tmp[80];

  /* update bytecount */

  sprintf(tmp, "Buffer: %d bytes", plotter_characters);
  n=0;
  XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple(tmp))); n++;
  XtSetValues(plotter_buffersize, wargs, n);

  XmStringFree(stmp);
}

static void plotout_papersize_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  plotter_letter = (long) client_data;
}

static void plotout_rotate_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  plotter_rotate = (long) client_data;
}

static void plotout_filedialog_create()
{
  XmString stmp;
  Widget papersize_label, papersize_pane,
	 center_label, center_check, 
	 autoup_label, autoup_check, 
	 autodown_label, autodown_check, 
	 rotate_menu, rotate_label, rotate_pane;
  Widget A3_w, A4_w, Letter_w;
  Widget rotate_selection[3];

  n=0;
  XtSetArg(wargs[n], XtNtitle, "PostScript File Selection"); n++;
  plotout_filedialog = XtAppCreateShell("plotout_filedialog", "plotout_filedialog", topLevelShellWidgetClass, XtDisplay(toplevel), wargs, n);

  n=0;
  plotout_fileform = XtCreateManagedWidget("form", xmFormWidgetClass, plotout_filedialog, wargs, n);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNpattern, (stmp=XmStringCreateSimple("*.ps"))); n++;
  plotout_fileselection = XtCreateManagedWidget("plotout_fileselection", xmFileSelectionBoxWidgetClass, plotout_fileform, wargs, n);
  XmStringFree(stmp);

  XtAddCallback(plotout_fileselection, XmNokCallback, plotout_fileopen_callback, (XtPointer)NULL);
  XtAddCallback(plotout_fileselection, XmNcancelCallback, plotout_fileclose_callback, (XtPointer)NULL);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNtopOffset, 10); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNleftWidget, plotout_fileselection); n++;
  XtSetArg(wargs[n], XmNleftOffset, 10); n++;
  papersize_label = XtCreateManagedWidget("Select paper size", xmLabelWidgetClass, plotout_fileform, wargs, n);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, papersize_label); n++;
  XtSetArg(wargs[n], XmNtopOffset, -8); n++;
  XtSetArg(wargs[n], XmNleftOffset, 10); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNleftWidget, papersize_label); n++;
  papersize = XmCreateOptionMenu(plotout_fileform, "papersize", wargs, n);

  n=0;
  papersize_pane = XmCreatePulldownMenu(papersize, "pulldown", wargs, n);

  n=0;
  XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateLocalized("A4"))); n++;
  A4_w = XtCreateManagedWidget("A4_select", xmPushButtonWidgetClass, papersize_pane, wargs, n);
  XtAddCallback(A4_w, XmNactivateCallback, plotout_papersize_callback, (XtPointer) 0);

  n=0;
  XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateLocalized("Letter"))); n++;
  Letter_w = XtCreateManagedWidget("letter_select", xmPushButtonWidgetClass, papersize_pane, wargs, n);
  XtAddCallback(Letter_w, XmNactivateCallback, plotout_papersize_callback, (XtPointer) 1);

  n=0;
  XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateLocalized("A3"))); n++;
  A3_w = XtCreateManagedWidget("A3_select", xmPushButtonWidgetClass, papersize_pane, wargs, n);
  XtAddCallback(A3_w, XmNactivateCallback, plotout_papersize_callback, (XtPointer) 2);

  n=0;
  XtSetArg(wargs[n], XmNsubMenuId, papersize_pane); n++;
  XtSetValues(papersize, wargs, n);

  XtManageChild(papersize);

  n=0;
  XtSetArg(wargs[n], XmNmenuHistory, (plotter_letter==1)?Letter_w:(plotter_letter==2?A3_w:A4_w)); n++;
  XtSetValues(papersize, wargs, n);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, papersize); n++;
  XtSetArg(wargs[n], XmNtopOffset, 10); n++;
  XtSetArg(wargs[n], XmNleftOffset, 10); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNleftWidget, plotout_fileselection); n++;
  center_label = XtCreateManagedWidget("Center plot         ", xmLabelWidgetClass, plotout_fileform, wargs, n);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, center_label); n++;
  XtSetArg(wargs[n], XmNtopOffset, -8); n++;
  XtSetArg(wargs[n], XmNleftOffset, 10); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNleftWidget, center_label); n++;
  center_check = XmCreateSimpleCheckBox(plotout_fileform, "center_check", wargs, n);
  XtManageChild(center_check);

  n=0;
#if XmVERSION==1
  XtSetArg(wargs[n], XmNindicatorOn, True); n++;
  XtSetArg(wargs[n], XmNset, plotter_center?True:False); n++;
#else
  XtSetArg(wargs[n], XmNindicatorOn, XmINDICATOR_CHECK_BOX); n++;
  XtSetArg(wargs[n], XmNset, plotter_center?XmSET:XmUNSET); n++;
#endif
  XtSetArg(wargs[n], XmNindicatorSize, 20); n++;
  plotter_center_select = XmCreateToggleButton(center_check, "", wargs, n);
  XtManageChild(plotter_center_select);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, center_label); n++;
  XtSetArg(wargs[n], XmNtopOffset, 10); n++;
  XtSetArg(wargs[n], XmNleftOffset, 10); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNleftWidget, plotout_fileselection); n++;
  autoup_label = XtCreateManagedWidget("Scale up to fit page", xmLabelWidgetClass, plotout_fileform, wargs, n);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, autoup_label); n++;
  XtSetArg(wargs[n], XmNtopOffset, -8); n++;
  XtSetArg(wargs[n], XmNleftOffset, 10); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNleftWidget, center_label); n++;
  autoup_check = XmCreateSimpleCheckBox(plotout_fileform, "autoup_check", wargs, n);
  XtManageChild(autoup_check);

  n=0;
#if XmVERSION==1
  XtSetArg(wargs[n], XmNindicatorOn, True); n++;
  XtSetArg(wargs[n], XmNset, plotter_autoup?True:False); n++;
#else
  XtSetArg(wargs[n], XmNindicatorOn, XmINDICATOR_CHECK_BOX); n++;
  XtSetArg(wargs[n], XmNset, plotter_autoup?XmSET:XmUNSET); n++;
#endif
  XtSetArg(wargs[n], XmNindicatorSize, 20); n++;
  plotter_autoup_select = XmCreateToggleButton(autoup_check, "", wargs, n);
  XtManageChild(plotter_autoup_select);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, autoup_label); n++;
  XtSetArg(wargs[n], XmNtopOffset, 10); n++;
  XtSetArg(wargs[n], XmNleftOffset, 10); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNleftWidget, plotout_fileselection); n++;
  autodown_label = XtCreateManagedWidget("Scale down to fit page", xmLabelWidgetClass, plotout_fileform, wargs, n);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, autodown_label); n++;
  XtSetArg(wargs[n], XmNtopOffset, -8); n++;
  XtSetArg(wargs[n], XmNleftOffset, 10); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNleftWidget, center_label); n++;
  autodown_check = XmCreateSimpleCheckBox(plotout_fileform, "autodown_check", wargs, n);
  XtManageChild(autodown_check);

  n=0;
#if XmVERSION==1
  XtSetArg(wargs[n], XmNindicatorOn, True); n++;
  XtSetArg(wargs[n], XmNset, plotter_autodown?True:False); n++;
#else
  XtSetArg(wargs[n], XmNindicatorOn, XmINDICATOR_CHECK_BOX); n++;
  XtSetArg(wargs[n], XmNset, plotter_autodown?XmSET:XmUNSET); n++;
#endif
  XtSetArg(wargs[n], XmNindicatorSize, 20); n++;
  plotter_autodown_select = XmCreateToggleButton(autodown_check, "", wargs, n);
  XtManageChild(plotter_autodown_select);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, autodown_check); n++;
  XtSetArg(wargs[n], XmNtopOffset, 10); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNleftWidget, plotout_fileselection); n++;
  XtSetArg(wargs[n], XmNleftOffset, 10); n++;
  rotate_label = XtCreateManagedWidget("Rotate 90 degrees", xmLabelWidgetClass, plotout_fileform, wargs, n);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, rotate_label); n++;
  XtSetArg(wargs[n], XmNtopOffset, -8); n++;
  XtSetArg(wargs[n], XmNleftOffset, 10); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNleftWidget, rotate_label); n++;
  rotate_menu = XmCreateOptionMenu(plotout_fileform, "rotate", wargs, n);

  n=0;
  rotate_pane = XmCreatePulldownMenu(rotate_menu, "pulldown", wargs, n);

  n=0;
  XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateLocalized("No"))); n++;
  rotate_selection[0] = XtCreateManagedWidget("no_select", xmPushButtonWidgetClass, rotate_pane, wargs, n);
  XtAddCallback(rotate_selection[0], XmNactivateCallback, plotout_rotate_callback, (XtPointer) 0);

  n=0;
  XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateLocalized("Yes"))); n++;
  rotate_selection[1] = XtCreateManagedWidget("no_select", xmPushButtonWidgetClass, rotate_pane, wargs, n);
  XtAddCallback(rotate_selection[1], XmNactivateCallback, plotout_rotate_callback, (XtPointer) 1);

  n=0;
  XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateLocalized("Auto"))); n++;
  rotate_selection[2] = XtCreateManagedWidget("no_select", xmPushButtonWidgetClass, rotate_pane, wargs, n);
  XtAddCallback(rotate_selection[2], XmNactivateCallback, plotout_rotate_callback, (XtPointer) 2);

  n=0;
  XtSetArg(wargs[n], XmNsubMenuId, rotate_pane); n++;
  XtSetValues(rotate_menu, wargs, n);

  XtManageChild(rotate_menu);

  n=0;
  XtSetArg(wargs[n], XmNmenuHistory, rotate_selection[plotter_rotate]); n++;
  XtSetValues(rotate_menu, wargs, n);

  XtRealizeWidget(plotout_filedialog);

  XmFileSelectionDoSearch(plotout_fileselection, NULL);
}

static void plotout_filedialog_close()
{
  XtDestroyWidget(plotout_filedialog);
}

static void plotter_checklimit()
{
  if(plotter_x < plotter_xmin) plotter_xmin = plotter_x;
  if(plotter_x > plotter_xmax) plotter_xmax = plotter_x;
  if(plotter_y < plotter_ymin) plotter_ymin = plotter_y;
  if(plotter_y > plotter_ymax) plotter_ymax = plotter_y;
}

void plotter_SY(char c)
{
  if(plotter_wnd == NULL) plotter_init();
  if(c == 16)
  {
    /* pen up */
    if(debug&DEBUGinterface) fprintf(debug_fh, "plotter_SY: pen up\n");
    plotter_pendown=0;
  }
  else if(c == 32)
  {
    /* pen down */
    if(debug&DEBUGinterface) fprintf(debug_fh, "plotter_SY: pen down\n");
    plotter_pendown=1;
    plotter_newpath();
  }
  else
  {
    if(plotter_pendown)
    {
      if(debug&DEBUGinterface) fprintf(debug_fh, "plotter_SY: draw\n");
      if(plotter_firstline==NULL)
      {
	if(debug&DEBUGinterface) fprintf(debug_fh, "plotter_SY: first buffer\n");
	plotter_newpath();
      }
      if(plotter_currentline->buffersize >= PLOTTERLINE_SIZE)
      {
	if(debug&DEBUGinterface) fprintf(debug_fh, "plotter_SY: buffer full\n");
	plotter_newpath();
      }
      if(plotter_currentline->buffersize == 0)
      {
	if(debug&DEBUGinterface) fprintf(debug_fh, "plotter_SY: first time buffer is used\n");
	plotter_currentline->linewidth = plotter_linewidth;
	plotter_currentline->linecolor = plotter_linecolor;
	plotter_currentline->x = plotter_x;
	plotter_currentline->y = plotter_y;
	plotter_checklimit();
	plotter_update();
      }
      plotter_currentline->buffer[plotter_currentline->buffersize++] = c;
      plotter_characters++;
      plotter_x += plotter_dx[c];
      plotter_y += plotter_dy[c];
      plotter_checklimit();
    }
    else
    {
      if(debug&DEBUGinterface) fprintf(debug_fh, "plotter_SY: move\n");
      plotter_x += plotter_dx[c];
      plotter_y += plotter_dy[c];
    }
  }
  if(debug&DEBUGinterface) fprintf(debug_fh, "plotter_SY: xmin: %d xmax: %d ymin: %d ymax: %d\n",
      plotter_xmin, plotter_xmax, plotter_ymin, plotter_ymax);
}

static void plotter_coord_init(PLOTTERMATRIX *a)
{
  /* Load unit transform */
  a->v[0] = 0;
  a->v[1] = 0;
  a->a[0][0] = 1;
  a->a[1][1] = 1;
  a->a[0][1] = 0;
  a->a[1][0] = 0;
}

static double plotter_coord_x(PLOTTERMATRIX *a, double x, double y)
{
  return a->v[0] + a->a[0][0]*x + a->a[0][1]*y;
}

static double plotter_coord_y(PLOTTERMATRIX *a, double x, double y)
{
  return a->v[1] + a->a[1][0]*x + a->a[1][1]*y;
}

static void plotter_coord_mult(PLOTTERMATRIX *a, PLOTTERMATRIX *b, PLOTTERMATRIX *c)
{
  PLOTTERMATRIX bc;
  /* a = b*c, a may be b or c  */
  if(debug&DEBUGinterface) fprintf(debug_fh, "a = b*c:\nb:\t%g\t%g\t%g\n\t%g\t%g\t%g\n",
      b->v[0], b->a[0][0], b->a[0][1], b->v[1], b->a[1][0], b->a[1][1]);
  if(debug&DEBUGinterface) fprintf(debug_fh, "c:\t%g\t%g\t%g\n\t%g\t%g\t%g\n",
      c->v[0], c->a[0][0], c->a[0][1], c->v[1], c->a[1][0], c->a[1][1]);
  bc.a[0][0] = b->a[0][0]*c->a[0][0] + b->a[0][1]*c->a[1][0];
  bc.a[0][1] = b->a[0][0]*c->a[0][1] + b->a[0][1]*c->a[1][1];
  bc.a[1][0] = b->a[1][0]*c->a[0][0] + b->a[1][1]*c->a[1][0];
  bc.a[1][1] = b->a[1][0]*c->a[0][1] + b->a[1][1]*c->a[1][1];
  bc.v[0] = plotter_coord_x(b, c->v[0], c->v[1]);
  bc.v[1] = plotter_coord_y(b, c->v[0], c->v[1]);
  a->a[0][0] = bc.a[0][0];
  a->a[0][1] = bc.a[0][1];
  a->a[1][0] = bc.a[1][0];
  a->a[1][1] = bc.a[1][1];
  a->v[0] = bc.v[0];
  a->v[1] = bc.v[1];
  if(debug&DEBUGinterface) fprintf(debug_fh, "a:\t%g\t%g\t%g\n\t%g\t%g\t%g\n",
      a->v[0], a->a[0][0], a->a[0][1], a->v[1], a->a[1][0], a->a[1][1]);
}

static void plotter_coord_rotate(PLOTTERMATRIX *a, double angle)
{
  PLOTTERMATRIX m1;
  plotter_coord_init(&m1);
  m1.a[0][0] = cos(angle);
  m1.a[1][0] = sin(angle);
  m1.a[0][1] = -sin(angle);
  m1.a[1][1] = cos(angle);
  plotter_coord_mult(a, a, &m1);
}

static void plotter_coord_scale(PLOTTERMATRIX *a, double scale)
{
  PLOTTERMATRIX m1;
  plotter_coord_init(&m1);
  m1.a[0][0] = scale;
  m1.a[1][1] = scale;
  plotter_coord_mult(a, a, &m1);
}

static void plotter_coord_translate(PLOTTERMATRIX *a, double x, double y)
{
  PLOTTERMATRIX m1;
  plotter_coord_init(&m1);
  m1.v[0] = x;
  m1.v[1] = y;
  plotter_coord_mult(a, a, &m1);
}

static void plotter_coord_dump(PLOTTERMATRIX *a, char *txt)
{
  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "plotter_coord_dump: %s\n", txt);
    fprintf(debug_fh, "(  0,   0) -> (%g, %g)\n", plotter_coord_x(a,   0.0,   0.0), plotter_coord_y(a,   0.0,   0.0));
    fprintf(debug_fh, "(100, 100) -> (%g, %g)\n", plotter_coord_x(a, 100.0, 100.0), plotter_coord_y(a, 100.0, 100.0));
    fprintf(debug_fh, "(100, 200) -> (%g, %g)\n", plotter_coord_x(a, 100.0, 200.0), plotter_coord_y(a, 100.0, 200.0));
    fprintf(debug_fh, "(200, 100) -> (%g, %g)\n", plotter_coord_x(a, 200.0, 100.0), plotter_coord_y(a, 200.0, 100.0));
    fprintf(debug_fh, "(200, 200) -> (%g, %g)\n", plotter_coord_x(a, 200.0, 200.0), plotter_coord_y(a, 200.0, 200.0));
  }
}

static void plotout_fileopen_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  XmFileSelectionBoxCallbackStruct *cbs = (XmFileSelectionBoxCallbackStruct *)call_data;
  char *filename;
  char *s;
  FILE *plotter_fh;
  double dev_xmin, dev_xmax, dev_ymin, dev_ymax;
  double xscale, yscale, scale;
  double dtmp1, dtmp2;
  double shift_x, shift_y;
  char *paper_text;
  PLOTTERLINE *line, *next;
  int do_rotate, i;
  PLOTTERMATRIX m;
  int x,y;
  double unit,xunit,yunit;

  XmStringGetLtoR(cbs->value, XmSTRING_DEFAULT_CHARSET, &filename);
  plotter_fh = fopen(filename, "w");
  if(plotter_fh != NULL)
  {

    /* Get parameters */

    n=0;
    plotter_center=0;
    XtSetArg(wargs[n], XmNset, &plotter_center); n++;
    XtGetValues(plotter_center_select, wargs, n);

    n=0;
    plotter_autoup=0;
    XtSetArg(wargs[n], XmNset, &plotter_autoup); n++;
    XtGetValues(plotter_autoup_select, wargs, n);

    n=0;
    plotter_autodown=0;
    XtSetArg(wargs[n], XmNset, &plotter_autodown); n++;
    XtGetValues(plotter_autodown_select, wargs, n);

    if(debug&DEBUGinterface)
    {
      fprintf(debug_fh, "plotter_center: %d\n", plotter_center);
      fprintf(debug_fh, "plotter_autoup: %d\n", plotter_autoup);
      fprintf(debug_fh, "plotter_autodown: %d\n", plotter_autodown);
      fprintf(debug_fh, "Opening file: %s\n", filename);
    }

    if(plotter_letter==1)
    {
      dev_xmin = 0;
      dev_xmax = 612;
      dev_ymin = 0;
      dev_ymax = 792;
      paper_text = "Letter";
    }
    else if(plotter_letter==2)
    {
      dev_xmin = 0;
      dev_xmax = 842;
      dev_ymin = 0;
      dev_ymax = 1190;
      paper_text = "A3";
    }
    else
    {
      dev_xmin = 0;
      dev_xmax = 595;
      dev_ymin = 0;
      dev_ymax = 842;
      paper_text = "A4";
    }

    fprintf(plotter_fh, "%%!PS-Adobe-3.0\n");
    fprintf(plotter_fh, "%%%%Creator: GIER simulator\n");
    fprintf(plotter_fh, "%%%%Pages: (atend)\n");
    fprintf(plotter_fh, "%%%%DocumentMedia: %s %.0f %.0f 0 white ()\n", paper_text, dev_xmax, dev_ymax);
    fprintf(plotter_fh, "%%%%BoundingBox: %.0f %.0f %.0f %.0f\n", dev_xmin, dev_ymin, dev_xmax, dev_ymax);
    fprintf(plotter_fh, "%%%%Orientation: Portrait\n");
    fprintf(plotter_fh, "%%%%EndComments\n\n");

    fprintf(plotter_fh, "[{\n");
    fprintf(plotter_fh, "%%%%BeginFeature: *PageSize %s\n", paper_text);
    fprintf(plotter_fh, "<</PageSize[%d %d]/ImagingBBox null>>setpagedevice\n", (int) dev_xmax, (int) dev_ymax);
    fprintf(plotter_fh, "%%%%EndFeature\n");
    fprintf(plotter_fh, "} stopped cleartomark\n");

    plotter_coord_init(&m);
    plotter_coord_dump(&m, "Unit matrix");
    
    plotter_coord_scale(&m, 72.0/254.0);
    plotter_coord_dump(&m, "After scale 0.1mm -> points");
    dev_xmin /= 72.0/254.0;
    dev_xmax /= 72.0/254.0;
    dev_ymin /= 72.0/254.0;
    dev_ymax /= 72.0/254.0;

    if(debug&DEBUGinterface) fprintf(debug_fh, "before rotate: dev_xmin: %g dev_xmax: %g dev_ymin: %g dev_ymax: %g\n", dev_xmin, dev_xmax, dev_ymin, dev_ymax);
    if(debug&DEBUGinterface) fprintf(debug_fh, "plotter_xmin: %d plotter_xmax: %d plotter_ymin: %d plotter_ymax: %d\n", plotter_xmin, plotter_xmax, plotter_ymin, plotter_ymax);

    do_rotate = plotter_rotate;
    if(plotter_rotate == 2)
    {
      do_rotate = ((dev_xmax-dev_xmin)>(dev_ymax-dev_ymin)) !=
		  ((plotter_xmax-plotter_xmin)>(plotter_ymax-plotter_ymin));
    }
    if(debug&DEBUGinterface) fprintf(debug_fh, "plotter out: do_rotate: %d\n", do_rotate);

    if(do_rotate)
    {
      plotter_coord_rotate(&m, M_PI_2);
      plotter_coord_dump(&m, "After rotate 90");
      plotter_coord_translate(&m, dev_ymin, -dev_xmax);
      plotter_coord_dump(&m, "After translate");
      dtmp1 = dev_xmin;
      dtmp2 = dev_xmax;
      dev_xmin = dev_ymin;
      dev_xmax = dev_ymax;
      dev_ymin = dtmp1;
      dev_ymax = dtmp2;
    }
    if(debug&DEBUGinterface) fprintf(debug_fh, "after rotate: dev_xmin: %g dev_xmax: %g dev_ymin: %g dev_ymax: %g\n", dev_xmin, dev_xmax, dev_ymin, dev_ymax);

    xscale = (dev_xmax-dev_xmin-254)/(plotter_xmax-plotter_xmin);
    yscale = (dev_ymax-dev_ymin-254)/(plotter_ymax-plotter_ymin);
    scale = xscale;
    if(yscale < scale) scale=yscale;
    if((scale>1.0 && plotter_autoup) ||
       (scale<1.0 && plotter_autodown))
    {
      if(debug&DEBUGinterface) fprintf(debug_fh, "plotter out: scale: %g\n", scale);
      plotter_coord_scale(&m, scale);
      plotter_coord_dump(&m, "After scale up/down");
      dev_xmin /= scale;
      dev_xmax /= scale;
      dev_ymin /= scale;
      dev_ymax /= scale;
    }

    /* center */

    if(plotter_center)
    {
      shift_x = (dev_xmin+dev_xmax)*0.5 -
		(plotter_xmax+plotter_xmin)*0.5;
      shift_y = (dev_ymin+dev_ymax)*0.5 -
		(plotter_ymax+plotter_ymin)*0.5;
      plotter_coord_translate(&m, shift_x, shift_y);
      plotter_coord_dump(&m, "After center");
      if(debug&DEBUGinterface) fprintf(debug_fh, "plotter out: center: %g %g\n", shift_x, shift_y);
    }
    
    fprintf(plotter_fh, "/p1 { 0 -1 rlineto} def\n");
    fprintf(plotter_fh, "/p3 {-1 -1 rlineto} def\n");
    fprintf(plotter_fh, "/p2 {-1  0 rlineto} def\n");
    fprintf(plotter_fh, "/pa {-1  1 rlineto} def\n");
    fprintf(plotter_fh, "/p8 { 0  1 rlineto} def\n");
    fprintf(plotter_fh, "/pc { 1  1 rlineto} def\n");
    fprintf(plotter_fh, "/p4 { 1  0 rlineto} def\n");
    fprintf(plotter_fh, "/p5 { 1 -1 rlineto} def\n");

    xunit = sqrt(m.a[0][0]*m.a[0][0] + m.a[1][0]*m.a[1][0]);
    yunit = sqrt(m.a[0][1]*m.a[0][1] + m.a[1][1]*m.a[1][1]);
    unit = (xunit+yunit)*0.5;
    if(debug&DEBUGinterface) fprintf(debug_fh, "plotter out: xunit: %g, yunit: %g, unit: %g\n", 
	xunit, yunit, unit);

    line = plotter_firstline;
    while(line)
    {
      if(line->buffersize>0)
      {
	x=line->x;
	y=line->y;
	fprintf(plotter_fh, "1 setlinejoin\n");
	fprintf(plotter_fh, "1 setlinecap\n");
	fprintf(plotter_fh, "newpath %g %g moveto\n", plotter_coord_x(&m, (double)x, (double)y),
	    plotter_coord_y(&m, (double)x, (double)y));
	fprintf(plotter_fh, "%g setlinewidth\n", unit*((double)line->linewidth));
	switch(line->linecolor)
	{
	  case 0:
	    fprintf(plotter_fh, "0 0 0 setrgbcolor\n");
	    break;
	  case 1:
	    fprintf(plotter_fh, "1 0 0 setrgbcolor\n");
	    break;
	  case 2:
	    fprintf(plotter_fh, "0 1 0 setrgbcolor\n");
	    break;
	  case 3:
	    fprintf(plotter_fh, "0 0 1 setrgbcolor\n");
	    break;
	}
	for(i=0; i<line->buffersize; i++)
	{
	  fprintf(plotter_fh, "%g %g lineto\n", plotter_coord_x(&m, (double)x, (double)y),
	      plotter_coord_y(&m, (double)x, (double)y));
	  x += plotter_dx[line->buffer[i]];
	  y += plotter_dy[line->buffer[i]];
	}
	fprintf(plotter_fh, "stroke\n");
      }

      next = line->nextline;
      free((char *) line);
      line = next;
    }
    fprintf(plotter_fh, "showpage\n");
    fprintf(plotter_fh, "%%%%Pages: 1\n");
    fprintf(plotter_fh, "%%%%EOF\n%c", (char) 4);
    fclose(plotter_fh);
    plotter_characters=0;
    plotter_firstline=NULL;
    plotter_currentline=NULL;
    plotter_x=0;
    plotter_y=0;
    plotter_xmin=99999999;
    plotter_xmax=-99999999;
    plotter_ymin=99999999;
    plotter_ymax=-99999999;
    plotter_update();
  }
  plotout_filedialog_close();

  XtFree(filename);
}

static void plotter_newpath()
{
  PLOTTERLINE *nextline;
  if(plotter_firstline == NULL)
  {
    if(debug&DEBUGinterface) fprintf(debug_fh, "plotter_newpath: first buffer\n");
    plotter_firstline = (PLOTTERLINE *) malloc(sizeof(*nextline));
    plotter_currentline = plotter_firstline;
    plotter_currentline->nextline = NULL;
    plotter_currentline->buffersize = 0;
  }
  else if(plotter_currentline->buffersize != 0)
  {
    /*
     * Don't start a new buffer if the current one
     * hasn't been used
     */
    
    if(debug&DEBUGinterface) fprintf(debug_fh, "plotter_newpath: Start on new buffer\n");
    nextline = (PLOTTERLINE *) malloc(sizeof(*nextline));
    plotter_currentline->nextline = nextline;
    plotter_currentline = nextline;
    plotter_currentline->nextline = NULL;
    plotter_currentline->buffersize = 0;
  }
}

static void plotter_change_setup()
{
  if(plotter_firstline != NULL)
  {
    if(plotter_currentline->buffersize != 0) plotter_newpath();
  }
}

static void plotter_linecolor_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  int last_plotter_linecolor;

  last_plotter_linecolor=plotter_linecolor;
  plotter_linecolor = (long) client_data;
  if(debug&DEBUGinterface) fprintf(debug_fh, "Line color set to: %d\n", plotter_linecolor);
  if(plotter_linecolor != last_plotter_linecolor) plotter_change_setup();
}

static void plotter_linewidth_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  int last_plotter_linewidth;
  char *s;
  last_plotter_linewidth = plotter_linewidth;
  s = XmTextFieldGetString(w);
  if(debug&DEBUGinterface) fprintf(debug_fh, "linewidth_buffer: '%s'\n", s);
  if(s[0] != '\0') sscanf(s, "%d", &plotter_linewidth);
  if(debug&DEBUGinterface) fprintf(debug_fh, "linewidth: '%d'\n", plotter_linewidth);
  XtFree(s);
  if(plotter_linewidth != last_plotter_linewidth) plotter_change_setup();
}

void plotter_set_pen(int color,int width)
{
  if(plotter_wnd==NULL) return;
  plotter_linecolor = color;
  n=0;
  XtSetArg(wargs[n], XmNmenuHistory, plotter_linecolor_w[plotter_linecolor]); n++;
  XtSetValues(plotter_linecolor_select, wargs, n);

  plotter_linewidth = width;
  n=0;
  sprintf(plotter_linewidth_buffer, "%d", plotter_linewidth);
  XtSetArg(wargs[n], XmNvalue, plotter_linewidth_buffer); n++;
  XtSetValues(plotter_linewidth_edit, wargs, n);

  plotter_change_setup();
}

static void plotter_plot_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  plotout_filedialog_create();
}

static void plotter_cancelplot_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  plotter_characters=0;
  plotter_firstline=NULL;
  plotter_currentline=NULL;
  plotter_x=0;
  plotter_y=0;
  plotter_xmin=99999999;
  plotter_xmax=-99999999;
  plotter_ymin=99999999;
  plotter_ymax=-99999999;
  plotter_update();
}

static void plotout_fileclose_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  plotout_filedialog_close();
}

static void plotter_endfile_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  int i;

  plotter_update();
}

static void plotter_newfile_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  plotter_endfile_callback(w, client_data, call_data);
  plotout_filedialog_create();
}

static void plotter_destroy_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  plotter_wnd = NULL;

  update_viewmenu();
}


void plotter_init()
{
  int st;
  int i,j,k;
  double x;
  Widget plotter_linewidth_label,
	 plotter_linecolor_label;
  XmString stmp,stmp2;

  if(plotter_wnd == NULL)
  {
    n=0;
    XtSetArg(wargs[n], XtNtitle, "GIER Plotter"); n++;
    XtSetArg(wargs[n], XtNgeometry, "+175+375"); n++;
    plotter_wnd = XtAppCreateShell("plotter", "plotter", topLevelShellWidgetClass, XtDisplay(toplevel), wargs, n);
    XtAddCallback(plotter_wnd, XtNdestroyCallback, plotter_destroy_callback, NULL);

    n=0;
    plotter_form=XtCreateManagedWidget("plotter_form", xmFormWidgetClass, plotter_wnd, wargs, n);

    n=0;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple("Plot"))); n++;
    plotter_plot = XtCreateManagedWidget("plotter_plot", xmPushButtonWidgetClass, plotter_form, wargs, n);
    XtAddCallback(plotter_plot, XmNactivateCallback, plotter_plot_callback, NULL);
    XmStringFree(stmp);

    n=0;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
    XtSetArg(wargs[n], XmNleftWidget, plotter_plot); n++;
    XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple("Cancel plot"))); n++;
    plotter_cancelplot = XtCreateManagedWidget("plotter_cancelplot", xmPushButtonWidgetClass, plotter_form, wargs, n);
    XtAddCallback(plotter_cancelplot, XmNactivateCallback, plotter_cancelplot_callback, NULL);
    XmStringFree(stmp);

    n=0;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
    XtSetArg(wargs[n], XmNtopWidget, plotter_plot); n++;
    XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNrightAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNalignment, XmALIGNMENT_BEGINNING); n++;
    XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple("Buffer: 0 bytes"))); n++;
    plotter_buffersize = XtCreateManagedWidget("plotter_buffersize", xmLabelWidgetClass, plotter_form, wargs, n);
    XmStringFree(stmp);

    n=0;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
    XtSetArg(wargs[n], XmNtopWidget, plotter_buffersize); n++;
    XtSetArg(wargs[n], XmNtopOffset, 10); n++;
    XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple("Linewidth, 0.1 mm's"))); n++;
    plotter_linewidth_label = XtCreateManagedWidget("plotter_linewidth_label", xmLabelWidgetClass, plotter_form, wargs, n);
    XmStringFree(stmp);

    n=0;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
    XtSetArg(wargs[n], XmNtopWidget, plotter_linewidth_label); n++;
    XtSetArg(wargs[n], XmNtopOffset, -4); n++;
    XtSetArg(wargs[n], XmNleftOffset, 10); n++;
    XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
    XtSetArg(wargs[n], XmNleftWidget, plotter_linewidth_label); n++;
    XtSetArg(wargs[n], XmNvalue, plotter_linewidth_buffer); n++;
    sprintf(plotter_linewidth_buffer, "%d", plotter_linewidth);
    XtSetArg(wargs[n], XmNmaxLength, sizeof(plotter_linewidth_buffer)-1); n++;
    XtSetArg(wargs[n], XmNcolumns, sizeof(plotter_linewidth_buffer)-1); n++;
    XtSetArg(wargs[n], XmNverifyBell, False); n++;

    plotter_linewidth_edit = XtCreateManagedWidget("plotter_linewidth_edit", xmTextFieldWidgetClass, plotter_form, wargs, n);
    XtAddCallback(plotter_linewidth_edit, XmNvalueChangedCallback, plotter_linewidth_callback, NULL);

    n=0;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
    XtSetArg(wargs[n], XmNtopWidget, plotter_linewidth_label); n++;
    XtSetArg(wargs[n], XmNtopOffset, 10); n++;
    XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple("Line color:"))); n++;
    plotter_linecolor_label = XtCreateManagedWidget("plotter_linecolor_label", xmLabelWidgetClass, plotter_form, wargs, n);
    XmStringFree(stmp);

    n=0;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
    XtSetArg(wargs[n], XmNtopWidget, plotter_linecolor_label); n++;
    XtSetArg(wargs[n], XmNleftOffset, 0); n++;
    XtSetArg(wargs[n], XmNtopOffset, -4); n++;
    XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
    XtSetArg(wargs[n], XmNleftWidget, plotter_linewidth_edit); n++;
    plotter_linecolor_select = XmCreateOptionMenu(plotter_form, "plotter_linecolor_select", wargs, n);

    n=0;
    plotter_linecolor_pane = XmCreatePulldownMenu(plotter_linecolor_select, "pulldown", wargs, n);

    n=0;
    XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateLocalized("Black"))); n++;
    plotter_linecolor_w[0] = XtCreateManagedWidget("plotter_linecolor_black", xmPushButtonWidgetClass, plotter_linecolor_pane, wargs, n);
    XmStringFree(stmp);
    XtAddCallback(plotter_linecolor_w[0], XmNactivateCallback, plotter_linecolor_callback, (XtPointer) 0);

    n=0;
    XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateLocalized("Red"))); n++;
    plotter_linecolor_w[1] = XtCreateManagedWidget("plotter_linecolor_red", xmPushButtonWidgetClass, plotter_linecolor_pane, wargs, n);
    XmStringFree(stmp);
    XtAddCallback(plotter_linecolor_w[1], XmNactivateCallback, plotter_linecolor_callback, (XtPointer) 1);

    n=0;
    XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateLocalized("Green"))); n++;
    plotter_linecolor_w[2] = XtCreateManagedWidget("plotter_linecolor_green", xmPushButtonWidgetClass, plotter_linecolor_pane, wargs, n);
    XmStringFree(stmp);
    XtAddCallback(plotter_linecolor_w[2], XmNactivateCallback, plotter_linecolor_callback, (XtPointer) 2);

    n=0;
    XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateLocalized("Blue"))); n++;
    plotter_linecolor_w[3] = XtCreateManagedWidget("plotter_linecolor_blue", xmPushButtonWidgetClass, plotter_linecolor_pane, wargs, n);
    XmStringFree(stmp);
    XtAddCallback(plotter_linecolor_w[3], XmNactivateCallback, plotter_linecolor_callback, (XtPointer) 3);

    n=0;
    XtSetArg(wargs[n], XmNsubMenuId, plotter_linecolor_pane); n++;
    XtSetValues(plotter_linecolor_select, wargs, n);

    XtManageChild(plotter_linecolor_select);

    n=0;
    XtSetArg(wargs[n], XmNmenuHistory, plotter_linecolor_w[plotter_linecolor]); n++;
    XtSetValues(plotter_linecolor_select, wargs, n);

    XtRealizeWidget(plotter_wnd);
  }
}

void plotter_destroy()
{
  XtUnrealizeWidget(plotter_wnd);
  XtDestroyWidget(plotter_wnd);
}

int plotter_is_visible()
{
  return(plotter_wnd!=NULL);
}

void plotout_init()
{
  int st;
  int i,j,k;
  double x;
  XmString stmp,stmp2;

  if(plotout_wnd != NULL)
  {
  }
  else
  {
    n=0;
    XtSetArg(wargs[n], XtNtitle, "GIER Plotter"); n++;
    XtSetArg(wargs[n], XtNgeometry, "+175+375"); n++;
    plotter_wnd = XtAppCreateShell("plotter", "plotter", topLevelShellWidgetClass, XtDisplay(toplevel), wargs, n);
    XtAddCallback(plotter_wnd, XtNdestroyCallback, plotter_destroy_callback, NULL);

    n=0;
    plotter_form=XtCreateManagedWidget("plotter_form", xmFormWidgetClass, plotter_wnd, wargs, n);

    n=0;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple("New File"))); n++;
    plotter_newfile = XtCreateManagedWidget("plotter_newfile", xmPushButtonWidgetClass, plotter_form, wargs, n);
    XtAddCallback(plotter_newfile, XmNactivateCallback, plotter_newfile_callback, NULL);
    XmStringFree(stmp);

    n=0;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
    XtSetArg(wargs[n], XmNleftWidget, plotter_newfile); n++;
    XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple("End File"))); n++;
    plotter_endfile = XtCreateManagedWidget("plotter_endfile", xmPushButtonWidgetClass, plotter_form, wargs, n);
    XtAddCallback(plotter_endfile, XmNactivateCallback, plotter_endfile_callback, NULL);
    XmStringFree(stmp);

    n=0;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
    XtSetArg(wargs[n], XmNtopWidget, plotter_newfile); n++;
    XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNrightAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple("- no file -"))); n++;
    plotout_psfilename = XtCreateManagedWidget("plotout_psfilename", xmLabelWidgetClass, plotter_form, wargs, n);
    XmStringFree(stmp);

    XtRealizeWidget(plotter_wnd);

  }
}

/* Local variables */

typedef struct
{
  XFontStruct *textfont;
  Pixel tapebackground;
} punch_ApplicationData, *punch_ApplicationDataPtr;

static punch_ApplicationData punch_applicationdata;

static XtResource punch_resources[] =
{
  {"textfont", "Textfont", XtRFontStruct, sizeof(XFontStruct *),
   XtOffset(punch_ApplicationDataPtr, textfont),
#ifdef GIANTTYPEWRITER
   XtRString, "-adobe-courier-bold-r-*-*-*-180-100-100-*-*-*-*"
#else
   XtRString, "-adobe-courier-medium-r-*-*-*-180-75-75-*-*-*-*"
#endif
  },
  {"tapebackground", "Tapebackground", XtRPixel, sizeof(Pixel),
   XtOffset(punch_ApplicationDataPtr, tapebackground),
   XtRString, "gray"},
};

void punch_init();

static void punch_settapename(char *filename)
{
  XmString stmp;

  n=0;
  XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple(filename))); n++;
  XtSetValues(punch_tapename, wargs, n);

  XmStringFree(stmp);
}

static void punch_update()
{
  int i,j,k;
  char txt[PUNCH_TAPE_WINDOW+1];
  unsigned char c;
  int mask;
  int punch_uppercase;

  if(punch_wnd == NULL) return;

  txt[PUNCH_TAPE_WINDOW] = '\0';

  for(j=0; j<PUNCH_TAPE_WINDOW; j++)
  {
    punch_uppercase = 0;
    c = flx2a(remove_parity((unsigned char) punch_char_table[j]), &punch_uppercase);
    if(c==255) c=' ';
    txt[j] = c;
  }
  XDrawImageString(XtDisplay(punch_drawing), XtWindow(punch_drawing), punch_gray_gc, 0, punch_CursorY(0), txt, PUNCH_TAPE_WINDOW);

  mask = 1;
  for(i=0; i<9; i++)
  {
    if(i==3)
    {
      for(j=0; j<PUNCH_TAPE_WINDOW; j++) txt[j]='.';
    }
    else
    {
      for(j=0; j<PUNCH_TAPE_WINDOW; j++)
      {
	k=punch_char_table[j];
	txt[j]= (k&mask)?'o':' ';
      }
      mask=mask<<1;
    }
    XDrawImageString(XtDisplay(punch_drawing), XtWindow(punch_drawing), punch_hole_gc, 0, punch_CursorY(9-i), txt, PUNCH_TAPE_WINDOW);
    if(debug&DEBUGinterface) fprintf(debug_fh, "Print: %s\n", txt);
  }

  for(j=0; j<PUNCH_TAPE_WINDOW; j++) txt[j]=' ';
  txt[PUNCH_TAPE_WINDOW-1] = '^';
  XDrawImageString(XtDisplay(punch_drawing), XtWindow(punch_drawing), punch_gray_gc, 0, punch_CursorY(10), txt, PUNCH_TAPE_WINDOW);
}

void punch_SY(char c)
{
  int st,i;
  unsigned char c2;
  char noparc;
  char hashbuf[5];

  noparc = c;
  c = set_parity(c);

  while(punch_current_fd == -1) 
  {
    if(punch_wnd == NULL) punch_init();
    XmFileSelectionDoSearch(punch_filedialog, NULL);
    XtManageChild(punch_filedialog);
    while(punch_current_fd == -1 || XtAppPending(app))
    {
      XtAppProcessEvent(app,XtIMAll);
    }
  }

  if(punch_filetype == 0)
  {
    st=write(punch_current_fd, &c, 1);
  }
  else
  {
    c2 = flx2a(noparc, &punch_uppercase);
    if(c2 == 255)
    {
      if(noparc != 58 && noparc != 60)
      {
	sprintf(hashbuf, "#%3.3d", (int) noparc);
	st=write(punch_current_fd, hashbuf, 4);
      }
    }
    else
    {
      st=write(punch_current_fd, &c2, 1);
    }
  }

  if(st == -1)
  {
    close(punch_current_fd);
    punch_current_fd = -1;
    punch_settapename("- no tape -");
  }

  for(i=0; i<(PUNCH_TAPE_WINDOW-1); i++)
    punch_char_table[i] = punch_char_table[i+1];

  punch_char_table[PUNCH_TAPE_WINDOW-1] = c;

  punch_update();
}

static void punch_fileopen_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  XmFileSelectionBoxCallbackStruct *cbs = (XmFileSelectionBoxCallbackStruct *)call_data;
  char *filename;

  if(punch_current_fd != -1) close(punch_current_fd);

  XmStringGetLtoR(cbs->value, XmSTRING_DEFAULT_CHARSET, &filename);
  XtUnmanageChild(w);

  if(debug&DEBUGinterface) fprintf(debug_fh, "Opening file: %s\n", filename);

  punch_current_fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
  if(!strncmp(filename+strlen(filename)-3,"flx",3))
    punch_filetype = 0;
  else
    punch_filetype = 1;

  if(punch_current_fd != -1) punch_settapename(filename);
  XtFree(filename);
}

static void punch_fileclose_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  XtUnmanageChild(w);
}

static void endtape_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  int i;

  for(i=0; i<PUNCH_TAPE_WINDOW; i++) punch_char_table[i]=0;
  if(punch_current_fd != -1)
  {
    close(punch_current_fd);
    punch_current_fd = -1;
    punch_settapename("- no tape -");
  }
  punch_update();
}

static void newtape_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  endtape_callback(w, client_data, call_data);
  XmFileSelectionDoSearch(punch_filedialog, NULL);
  XtManageChild(punch_filedialog);
}

static void punch_expose_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  XmDrawingAreaCallbackStruct *cbstruct = (XmDrawingAreaCallbackStruct *) call_data;
  XEvent *event=cbstruct->event;

  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "punch_expose_callback called. Event type: %d\n", event->type);
    fprintf(debug_fh, "x: %d, y: %d\n", event->xexpose.x, event->xexpose.y);
    fprintf(debug_fh, "width: %d, height: %d\n", event->xexpose.width, event->xexpose.height);
  }

  punch_update();
}

static void punch_input_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  XmDrawingAreaCallbackStruct *cbstruct = (XmDrawingAreaCallbackStruct *) call_data;
  XEvent *event = cbstruct -> event;
  int i,ilen,keycode;

  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "punch_input_callback called.\n");
    fprintf(debug_fh, "event type: %d\n", event->type);
  }

}

static void punch_destroy_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
punch_wnd = NULL;

update_viewmenu();
}

void punch_init()
{
int st;
int i,j,k;
double x;
XGCValues values;
XtGCMask valueMask;
Colormap cmap;
XColor ignore;
XmString stmp,stmp2;
Position dx,dy;

if(punch_wnd != NULL)
{
}
else
{
  /* Get resources */

  XtGetApplicationResources(toplevel, &punch_applicationdata,
    punch_resources, XtNumber(punch_resources), NULL, 0);

  /*
      Font administration:
  */

  punch_font_width = punch_applicationdata.textfont->max_bounds.width;
  punch_font_height = punch_applicationdata.textfont->ascent+punch_applicationdata.textfont->descent;
  punch_drawing_width=punch_font_width*PUNCH_TAPE_WINDOW;
  punch_drawing_height=punch_font_height*11;

  n=0;
  XtSetArg(wargs[n], XtNtitle, "GIER Paper Tape Punch"); n++;
  XtSetArg(wargs[n], XtNgeometry, "+10+522"); n++;
  punch_wnd = XtAppCreateShell("punch", "punch", topLevelShellWidgetClass, XtDisplay(toplevel), wargs, n);
  XtAddCallback(punch_wnd, XtNdestroyCallback, punch_destroy_callback, NULL);

  n=0;
  punch_form=XtCreateManagedWidget("punch_form", xmFormWidgetClass, punch_wnd, wargs, n);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple("New Tape"))); n++;
  punch_newtape = XtCreateManagedWidget("punch_newtape", xmPushButtonWidgetClass, punch_form, wargs, n);
  XtAddCallback(punch_newtape, XmNactivateCallback, newtape_callback, NULL);
  XmStringFree(stmp);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNleftWidget, punch_newtape); n++;
  XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple("End Tape"))); n++;
  punch_endtape = XtCreateManagedWidget("punch_endtape", xmPushButtonWidgetClass, punch_form, wargs, n);
  XtAddCallback(punch_endtape, XmNactivateCallback, endtape_callback, NULL);
  XmStringFree(stmp);

  n=0;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, punch_newtape); n++;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNrightAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNlabelString, (stmp=XmStringCreateSimple("- no tape -"))); n++;
  punch_tapename = XtCreateManagedWidget("punch_tapename", xmLabelWidgetClass, punch_form, wargs, n);
  XmStringFree(stmp);

  n=0;
  XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
  XtSetArg(wargs[n], XmNtopWidget, punch_tapename); n++;
  XtSetArg(wargs[n], XmNrightAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNbottomAttachment, XmATTACH_FORM); n++;
  XtSetArg(wargs[n], XmNwidth, punch_drawing_width); n++;
  XtSetArg(wargs[n], XmNheight, punch_drawing_height); n++;
  XtSetArg(wargs[n], XmNbackground, punch_applicationdata.tapebackground); n++;
  punch_drawing=XtCreateManagedWidget("punch_drawing", xmDrawingAreaWidgetClass, punch_form, wargs, n);
  XtAddCallback(punch_drawing, XmNexposeCallback, punch_expose_callback, (XtPointer) NULL);
  XtAddCallback(punch_drawing, XmNinputCallback, punch_input_callback, (XtPointer) NULL);

  n=0;
  XtSetArg(wargs[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++;
  XtSetArg(wargs[n], XmNautoUnmanage, False); n++;
  XtSetArg(wargs[n], XmNnoResize, TRUE); n++;
  XtSetArg(wargs[n], XmNresizePolicy, XmRESIZE_NONE); n++;
  XtSetArg(wargs[n], XmNpattern, (stmp=XmStringCreateSimple("*"))); n++;
  XtSetArg(wargs[n], XmNdialogTitle, (stmp2=XmStringCreateSimple("Punch File Selection"))); n++;

  punch_filedialog = XmCreateFileSelectionDialog(punch_form, "FileDialog", wargs, n);
  XmStringFree(stmp);
  XmStringFree(stmp2);

  XtAddCallback(punch_filedialog, XmNokCallback, punch_fileopen_callback, (XtPointer)NULL);
  XtAddCallback(punch_filedialog, XmNcancelCallback, punch_fileclose_callback, (XtPointer)NULL);

  /*
      Create GC's:

      The paper is white, holes are black, and the background is gray:
  */

  valueMask = GCForeground | GCBackground | GCFont;
  values.foreground = BlackPixel(XtDisplay(punch_drawing),XDefaultScreen(XtDisplay(punch_drawing)));
  values.background = WhitePixel(XtDisplay(punch_drawing),XDefaultScreen(XtDisplay(punch_drawing)));
  values.font=punch_applicationdata.textfont->fid;
  punch_hole_gc = XtGetGC(punch_drawing, valueMask, &values);

  valueMask = GCForeground | GCBackground | GCFont;
  values.foreground = BlackPixel(XtDisplay(punch_drawing),XDefaultScreen(XtDisplay(punch_drawing)));
  values.background = punch_applicationdata.tapebackground;
  values.font=punch_applicationdata.textfont->fid;
  punch_gray_gc = XtGetGC(punch_drawing, valueMask, &values);

  for(i=0; i<PUNCH_TAPE_WINDOW; i++) punch_char_table[i]=0;
  punch_current_fd = -1;

  XtRealizeWidget(punch_wnd);

}
}

void punch_destroy()
{
XtUnrealizeWidget(punch_wnd);
XtDestroyWidget(punch_wnd);
}

int punch_is_visible()
{
return(punch_wnd != NULL);
}

typedef struct
{
Pixel lampcolors[8];
} nimbi_ApplicationData, *nimbi_ApplicationDataPtr;

static nimbi_ApplicationData nimbi_applicationdata;

static XtResource nimbi_resources[] =
{
{"lampcolor0", "Lampcolor0", XtRPixel, sizeof(Pixel),
 XtOffset(nimbi_ApplicationDataPtr, lampcolors[0]),
 XtRString, "#000000"},
{"lampcolor1", "Lampcolor1", XtRPixel, sizeof(Pixel),
 XtOffset(nimbi_ApplicationDataPtr, lampcolors[1]),
 XtRString, "#515151"},
{"lampcolor2", "Lampcolor2", XtRPixel, sizeof(Pixel),
 XtOffset(nimbi_ApplicationDataPtr, lampcolors[2]),
 XtRString, "#7A7A7A"},
{"lampcolor3", "Lampcolor3", XtRPixel, sizeof(Pixel),
 XtOffset(nimbi_ApplicationDataPtr, lampcolors[3]),
 XtRString, "#9A9A9A"},
{"lampcolor4", "Lampcolor4", XtRPixel, sizeof(Pixel),
 XtOffset(nimbi_ApplicationDataPtr, lampcolors[4]),
 XtRString, "#B7B7B7"},
{"lampcolor5", "Lampcolor5", XtRPixel, sizeof(Pixel),
 XtOffset(nimbi_ApplicationDataPtr, lampcolors[5]),
 XtRString, "#D1D1D1"},
{"lampcolor6", "Lampcolor6", XtRPixel, sizeof(Pixel),
 XtOffset(nimbi_ApplicationDataPtr, lampcolors[6]),
 XtRString, "#E8E8E8"},
{"lampcolor7", "Lampcolor7", XtRPixel, sizeof(Pixel),
 XtOffset(nimbi_ApplicationDataPtr, lampcolors[7]),
 XtRString, "#FFFFFF"},
};

static void nimbi_draw_bit(int x, int y, int intensity)
{
if(debug&DEBUGinterface) fprintf(debug_fh, "nimbi_draw_bit: %d %d %d %d\n", x, y, intensity, kb12_count);
XDrawArc(XtDisplay(nimbi_drawing), XtWindow(nimbi_drawing), nimbi_gc[0],
	 x-NIMBI_radius, y-NIMBI_radius,
	 NIMBI_radius*2, NIMBI_radius*2,
	 0, 360*64);
/* XFillArc(XtDisplay(nimbi_drawing), XtWindow(nimbi_drawing), nimbi_gc[intensity*7/kb12_count] */
XFillArc(XtDisplay(nimbi_drawing), XtWindow(nimbi_drawing), nimbi_gc[intensity?7:0],
	 x-NIMBI_radius, y-NIMBI_radius,
	 NIMBI_radius*2, NIMBI_radius*2,
	 0, 360*64);
}

void nimbi_update()
{
int i,changed;

if(debug&DEBUGinterface) fprintf(debug_fh, "nimbi_update: %d\n", kb12_count);
if(nimbi_wnd == NULL) return;
if(nimbi_being_destroyed) return;
if(kb12_count==0) kb12_count=1;

changed=0;
for(i=0;i<14;i++)
{
  if(nimbi_count[i] != last_nimbi_count[i]) changed=1;
  last_nimbi_count[i] = nimbi_count[i];
}
if(changed)
{
  nimbi_draw_bit(NIMBI_28x, NIMBI_28y, nimbi_count[0]);
  nimbi_draw_bit(NIMBI_29x, NIMBI_29y, nimbi_count[1]);
  nimbi_draw_bit(NIMBI_30x, NIMBI_30y, nimbi_count[2]);
  nimbi_draw_bit(NIMBI_31x, NIMBI_31y, nimbi_count[3]);
  nimbi_draw_bit(NIMBI_32x, NIMBI_32y, nimbi_count[4]);
  nimbi_draw_bit(NIMBI_33x, NIMBI_33y, nimbi_count[5]);
  nimbi_draw_bit(NIMBI_34x, NIMBI_34y, nimbi_count[6]);
  nimbi_draw_bit(NIMBI_35x, NIMBI_35y, nimbi_count[7]);
  nimbi_draw_bit(NIMBI_36x, NIMBI_36y, nimbi_count[8]);
  nimbi_draw_bit(NIMBI_37x, NIMBI_37y, nimbi_count[9]);
  nimbi_draw_bit(NIMBI_38x, NIMBI_38y, nimbi_count[10]);
  nimbi_draw_bit(NIMBI_39x, NIMBI_39y, nimbi_count[11]);
  nimbi_draw_bit(NIMBI_newgamex, NIMBI_newgamey, nimbi_count[12]);
  nimbi_draw_bit(NIMBI_answerx, NIMBI_answery, nimbi_count[13]);
  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "Nimbi changed: %lld", clock_count);
    for(i=0;i<14;i++)fprintf(debug_fh, " %d", nimbi_count[i]);
    fprintf(debug_fh, "\n");
  }
  XFlush(XtDisplay(nimbi_wnd));
}
}

static void nimbi_expose_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
if(debug&DEBUGinterface) fprintf(debug_fh, "nimbi_expose_callback called.\n");
last_nimbi_count[0] = -1;
nimbi_update();
}

static void nimbi_input_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  int i;
  int changed=0;
  XmDrawingAreaCallbackStruct *cbstruct = (XmDrawingAreaCallbackStruct *) call_data;
  XEvent *event = cbstruct -> event;
  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "nimbi_input_callback called.\n");
    fprintf(debug_fh, "event type: %d\n", event->type);
  }
  if(event->type == ButtonRelease)
  {
    if(debug&DEBUGinterface) fprintf(debug_fh, "x: %d y: %d button: %d state: %d\n", event->xbutton.x, event->xbutton.y, event->xbutton.button, event->xbutton.state);
    if(event->xbutton.button==1)
    {
      /* left mouse button */
      if(THISBUTTON(NIMBI_28x, NIMBI_28y))
      {
        MQ ^= ONE28;
	changed=1;
      }
      else if(THISBUTTON(NIMBI_29x, NIMBI_29y))
      {
        MQ ^= ONE29;
	changed=1;
      }
      else if(THISBUTTON(NIMBI_30x, NIMBI_30y))
      {
        MQ ^= ONE30;
	changed=1;
      }
      else if(THISBUTTON(NIMBI_31x, NIMBI_31y))
      {
        MQ ^= ONE31;
	changed=1;
      }
      else if(THISBUTTON(NIMBI_32x, NIMBI_32y))
      {
        MQ ^= ONE32;
	changed=1;
      }
      else if(THISBUTTON(NIMBI_33x, NIMBI_33y))
      {
        MQ ^= ONE33;
	changed=1;
      }
      else if(THISBUTTON(NIMBI_34x, NIMBI_34y))
      {
        MQ ^= ONE34;
	changed=1;
      }
      else if(THISBUTTON(NIMBI_35x, NIMBI_35y))
      {
        MQ ^= ONE35;
	changed=1;
      }
      else if(THISBUTTON(NIMBI_36x, NIMBI_36y))
      {
        MQ ^= ONE36;
	changed=1;
      }
      else if(THISBUTTON(NIMBI_37x, NIMBI_37y))
      {
        MQ ^= ONE37;
	changed=1;
      }
      else if(THISBUTTON(NIMBI_38x, NIMBI_38y))
      {
        MQ ^= ONE38;
	changed=1;
      }
      else if(THISBUTTON(NIMBI_39x, NIMBI_39y))
      {
        MQ ^= ONE39;
	changed=1;
      }
      else if(THISBUTTON(NIMBI_newgamex, NIMBI_newgamey))
      {
        IN ^= 0x200U;
	changed=1;
      }
      else if(THISBUTTON(NIMBI_answerx, NIMBI_answery))
      {
        IN ^= 0x100U;
	changed=1;
      }
    } /* Left button */
    if(changed)
    {
      kb12_reset();
      kb12_update();
      kb1_update();
      kb2_update();
      nimbi_update();
      kb12_reset();
    }
  } /* button release */
}

static void nimbi_resize_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  if(debug&DEBUGinterface) fprintf(debug_fh, "nimbi_resize_callback called.\n");
}

static void nimbi_destroy_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
  nimbi_wnd = NULL;
  if(debug&DEBUGinterface) fprintf(debug_fh, "nimbi_destroy_callback called.\n");
  update_viewmenu();
}

void nimbi_init()
{
  int st;
  int i,j;
  double x;
  XGCValues values;
  XtGCMask valueMask;
  XmString stmp;
  Window root_window;
  Pixmap bg,mask;
  XpmAttributes attrib;

  /* Force redraw */
  last_nimbi_count[0] = -1;
  if(nimbi_wnd != NULL)
  {
    XRaiseWindow(XtDisplay(nimbi_wnd), XtWindow(nimbi_wnd));
  }
  else
  {
    /* Get resources */

    nimbi_being_destroyed=0;
    XtGetApplicationResources(toplevel, &nimbi_applicationdata,
      nimbi_resources, XtNumber(nimbi_resources), NULL, 0);

    n=0;
    XtSetArg(wargs[n], XtNtitle, "NIMBI Board"); n++;
    XtSetArg(wargs[n], XtNgeometry, "+400+20"); n++;
    nimbi_wnd = XtAppCreateShell("nimbi", "nimbi", topLevelShellWidgetClass, XtDisplay(toplevel), wargs, n);
    XtAddCallback(nimbi_wnd, XtNdestroyCallback, nimbi_destroy_callback, (XtPointer)NULL);

    n=0;
    nimbi_form=XtCreateManagedWidget("nimbi_form", xmFormWidgetClass, nimbi_wnd, wargs, n);

    n=0;
    XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNrightAttachment, XmATTACH_FORM); n++;
    XtSetArg(wargs[n], XmNwidth, NIMBI_width); n++;
    XtSetArg(wargs[n], XmNheight, NIMBI_height); n++;
    nimbi_drawing=XtCreateManagedWidget("nimbi_drawing", xmDrawingAreaWidgetClass, nimbi_form, wargs, n);

    attrib.valuemask = XpmCloseness;
    attrib.closeness = 20000;
    root_window=XRootWindow(XtDisplay(nimbi_drawing),XDefaultScreen(XtDisplay(nimbi_drawing)));
    st=XpmCreatePixmapFromData(XtDisplay(nimbi_drawing), root_window, nimbi_xpm, &bg, NULL, &attrib);
    if(st != XpmSuccess)
    {
      if(debug&DEBUGinterface) fprintf(debug_fh, "XpmError: %s\n", XpmGetErrorString(st));
    }
    

    n=0;
    XtSetArg(wargs[n], XmNbackgroundPixmap, bg); n++;
    XtSetValues(nimbi_drawing, wargs, n);

    XtAddCallback(nimbi_drawing, XmNexposeCallback, nimbi_expose_callback, (XtPointer) NULL);
    XtAddCallback(nimbi_drawing, XmNinputCallback, nimbi_input_callback, (XtPointer) NULL);
    XtAddCallback(nimbi_drawing, XmNresizeCallback, nimbi_resize_callback, (XtPointer) NULL);

    for(i=0;i<8;i++)
    {
      valueMask = GCForeground | GCBackground | GCLineWidth;
      values.foreground = WhitePixel(XtDisplay(nimbi_drawing),XDefaultScreen(XtDisplay(nimbi_drawing)));
      values.foreground = nimbi_applicationdata.lampcolors[i];
      values.background = BlackPixel(XtDisplay(nimbi_drawing),XDefaultScreen(XtDisplay(nimbi_drawing)));
      values.line_width = 2;
      nimbi_gc[i] = XtGetGC(nimbi_drawing, valueMask, &values);
    }

    XtRealizeWidget(nimbi_wnd);
  }
}

void nimbi_destroy()
{
  if(debug&DEBUGinterface) fprintf(debug_fh,"nimbi_destroy called.\n");
  nimbi_being_destroyed=1;
  XtUnrealizeWidget(nimbi_wnd);
  XtDestroyWidget(nimbi_wnd);
}

int nimbi_is_visible()
{
  return(nimbi_wnd!=NULL);
}

void demotimeout(Widget w, XtIntervalId id)
{
  if(debug&DEBUGinterface) fprintf(debug_fh, "demotimeout called.\n");
  demomode=1;
  GIERdemocommand();
}

void GIERdemocommand()
{
  char *bp,*cp;
  int i;
  XmString stmp;

readagain:
  bp = fgets(demoline, sizeof(demoline)-1, demofile);
  if(bp == NULL)
  {
    fclose(demofile);
    demofile = NULL;
    demomode = 0;
    XtUnmanageChild(demo_wnd);
    XtDestroyWidget(demo_wnd);
    return;
  }
  for(i=0; demoline[i] && demoline[i]!='#'; i++);
  demoline[i]='\0';

  cp=strtok(bp, " \t\n");
  if(!cp) goto readagain;

  if(!strcasecmp(cp, "QUIT"))
  {
    GIER_exit();
  }
  else if(!strcasecmp(cp, "SHOW"))
  {
    cp=strtok(0, " \t\n");
    if(!strcasecmp(cp, "KB1"))
    {
      if(!kb1_is_visible()) kb1_init();
    }
    else if(!strcasecmp(cp, "KB2"))
    {
      if(!kb2_is_visible()) kb2_init();
    }
    else if(!strcasecmp(cp, "TYPEWRITER"))
    {
      if(!typewriter_is_visible()) typewriter_init();
    }
    else if(!strcasecmp(cp, "READER"))
    {
      if(!reader_is_visible()) reader_init();
    }
    else if(!strcasecmp(cp, "PUNCH"))
    {
      if(!punch_is_visible()) punch_init();
    }
    else if(!strcasecmp(cp, "PRINTER"))
    {
      if(!printer_is_visible()) printer_init();
    }
    else if(!strcasecmp(cp, "PLOTTER"))
    {
      if(!plotter_is_visible()) plotter_init();
    }
    else if(!strcasecmp(cp, "NIMBI"))
    {
      if(!nimbi_is_visible()) nimbi_init();
    }
    goto readagain;
  }
  else if(!strcasecmp(cp, "HIDE"))
  {
    cp=strtok(0, " \t\n");
    if(!strcasecmp(cp, "KB1"))
    {
      if(kb1_is_visible()) kb1_destroy();
    }
    else if(!strcasecmp(cp, "KB2"))
    {
      if(kb2_is_visible()) kb2_destroy();
    }
    else if(!strcasecmp(cp, "TYPEWRITER"))
    {
      if(typewriter_is_visible()) typewriter_destroy();
    }
    else if(!strcasecmp(cp, "READER"))
    {
      if(reader_is_visible()) reader_destroy();
    }
    else if(!strcasecmp(cp, "PUNCH"))
    {
      if(punch_is_visible()) punch_destroy();
    }
    else if(!strcasecmp(cp, "PRINTER"))
    {
      if(printer_is_visible()) printer_destroy();
    }
    else if(!strcasecmp(cp, "PLOTTER"))
    {
      if(plotter_is_visible()) plotter_destroy();
    }
    else if(!strcasecmp(cp, "NIMBI"))
    {
      if(nimbi_is_visible()) nimbi_destroy();
    }
    goto readagain;
  }
  else if(!strcasecmp(cp, "SLEEP"))
  {
    /* Read next demo command in x seconds */
    sscanf(strtok(0, " \t\n"), "%d", &demosleep);
    XtAppAddTimeOut(app, demosleep*1000, (XtTimerCallbackProc)demotimeout, (XtPointer) NULL);
    demomode=2;
    return;
  }
  else if(!strcasecmp(cp, "TYPE"))
  {
    cp=strtok(0, " \t\n");
    if(cp)
    {
      sscanf(cp, "%d", &demochar);
      if(YE_wait)
      {
	savechar(demochar);
	YE_wait=0;
	goto readagain;
      }
      else
      {
	printf("Demo: TYPE while not ready for input.\n");
      }
    }
  }
  else if(!strcasecmp(cp, "TEXT"))
  {
    n=0;
    cp=strtok(0, "\n");
    if(cp)
    {
      XtSetArg(wargs[n], XmNmessageString, (stmp=XmStringCreateLocalized(cp))); n++;
      XtSetValues(demo_wnd, wargs, n);
    }
    else
    {
      XtSetArg(wargs[n], XmNmessageString, (stmp=XmStringCreateLocalized(""))); n++;
      XtSetValues(demo_wnd, wargs, n);
    }
    XmStringFree(stmp);
    goto readagain;
  }
  else if(!strcasecmp(cp, "WAITINPUT"))
  {
    if(YE_wait)
    {
      goto readagain;
    }
    else
    {
      demomode=4;
      return;
    }
  }
  else if(!strcasecmp(cp, "WAITSTOP"))
  {
    demomode=5;
    return;
  }
  else if(!strcasecmp(cp, "WAIT"))
  {
    sscanf(strtok(0, " \t\n"), "%d", &demochar);
    demomode=3;
    return;
  }
  else if(!strcasecmp(cp, "LOADCONFIG"))
  {
    if(GIER_load(strtok(0, "\n"))==0)
    {
      fprintf(stderr, "Loading of the configuration failed.\n");
    }
    goto readagain;
  }
  else if(!strcasecmp(cp, "SAVECONFIG"))
  {
    GIER_save(strtok(0, "\n"));
    goto readagain;
  }
  else if(!strcasecmp(cp, "LOADREADER"))
  {
    reader_nchars=READER_TAPE_OFFSET;
    for(i=0; i<READER_TAPE_OFFSET; i++) reader_char_table[i]=0;
    if(reader_current_fd != -1) close(reader_current_fd);
    cp=strtok(0,"\n");
    reader_current_fd = open(cp, O_RDONLY);
    if(!strncmp(cp+strlen(cp)-3,"flx",3))
      reader_filetype=0;
    else
      reader_filetype=1;
    reader_update_buffer();
    reader_update();
    goto readagain;
  }
  else if(!strcasecmp(cp, "LOADPUNCH"))
  {
    if(punch_current_fd != -1) close(punch_current_fd);
    cp=strtok(0, "\n");
    punch_current_fd = open(cp, O_WRONLY|O_CREAT|O_TRUNC, 0666);
    if(punch_current_fd != -1) punch_settapename(cp);
    goto readagain;
  }
  else if(!strcasecmp(cp, "FINISHPUNCH"))
  {
    if(punch_current_fd != -1) close(punch_current_fd);
    punch_settapename("- no tape -");
    goto readagain;
  }
  else if(!strcasecmp(cp, "FINISHDEMO"))
  {
    fclose(demofile);
    demofile = NULL;
    demomode = 0;
    XtUnmanageChild(demo_wnd);
    XtDestroyWidget(demo_wnd);
  }
  else if(!strcasecmp(cp, "PRESS"))
  {
    cp=strtok(0, " \t\n");
    if(!strcasecmp(cp, "HP"))
    {
      HP_button_pressed=1;
      interface_update();
    }
    else if(!strcasecmp(cp, "STOP"))
    {
      normal_stop_pressed=1;
      interface_update();
    }
    else if(!strcasecmp(cp, "START"))
    {
      normal_start_pressed=1;
      interface_update();
    }
    else if(!strcasecmp(cp, "MSTOP"))
    {
      mikrotempi_stop_pressed=1;
      interface_update();
    }
    else if(!strcasecmp(cp, "MSTART"))
    {
      mikrotempi_start_pressed=1;
      interface_update();
    }
    else if(!strcasecmp(cp, "SINGLE"))
    {
      normal_singlestep_pressed=1;
      interface_update();
    }
    else
    {
      printf("Unknown demo PRESS %s\n", cp);
    }
    goto readagain;
  }
  else if(!strcasecmp(cp, "SOUND"))
  {
    cp=strtok(0, " \t\n");
    if(cp)
    {
      sscanf(cp, "%d", &i);
      if(i)
      {
	sound_enabled = 1;
      }
      else
      {
	sound_enabled = 0;
      }
      update_optionsmenu();
    }
    else
    {
      printf("Missing argument to SOUND.\n");
    }
    goto readagain;
  }
  else if(!strcasecmp(cp, "REWIND"))
  {
    fseek(demofile,0,SEEK_SET);
    goto readagain;
  }
  else
  {
    printf("Unknown demo command: %s\n", cp);
    goto readagain;
  }
  return;
}

static void GIERdemo(char *filename)
{
  XmString stmp;
  demomode=0;
  if(debug&DEBUGinterface) fprintf(debug_fh, "GIERdemo started with file name: %s\n", filename);
  demofile=fopen(filename, "r");
  if(demofile != NULL)
  {
    demomode=1;
    n = 0;
    XtSetArg(wargs[n], XtNtitle, "Demo"); n++;
    XtSetArg(wargs[n], XmNmessageString, (stmp=XmStringCreateLocalized("GIER demo mode."))); n++;
    XtSetArg(wargs[n], XmNdialogStyle, XmDIALOG_MODELESS); n++;
    XtSetArg(wargs[n], XmNautoUnmanage, FALSE); n++;
    XtSetArg(wargs[n], XmNnoResize, FALSE); n++;
    XtSetArg(wargs[n], XmNresizePolicy, XmRESIZE_GROW); n++;
    XtSetArg(wargs[n], XmNdeleteResponse, XmDESTROY); n++;
    demo_wnd = XmCreateInformationDialog(toplevel, "Demo", wargs, n);
    XmStringFree(stmp);

    XtUnmanageChild(XmMessageBoxGetChild(demo_wnd, XmDIALOG_HELP_BUTTON));
    XtUnmanageChild(XmMessageBoxGetChild(demo_wnd, XmDIALOG_OK_BUTTON));
    XtUnmanageChild(XmMessageBoxGetChild(demo_wnd, XmDIALOG_CANCEL_BUTTON));
    XtUnmanageChild(XmMessageBoxGetChild(demo_wnd, XmDIALOG_SYMBOL_LABEL));
    XtUnmanageChild(XmMessageBoxGetChild(demo_wnd, XmDIALOG_SEPARATOR));
				  
    XtManageChild(demo_wnd);
    GIERdemocommand();
  }
}

int main(int argc, char **argv)
{
  start_time=time(NULL);
  if(getenv("GIER_CLOCK")) gier_clock=atoi(getenv("GIER_CLOCK"));
  if(getenv("DRUM_SPEED")) sscanf(getenv("DRUM_SPEED"), "%lg", &drum_speed);
  drum_clock = ((double)gier_clock)/drum_speed/40.0;
  fprintf(stderr, "GIER_CLOCK: %d\n", gier_clock);
  fprintf(stderr, "DRUM_CLOCK: %lg\n", drum_clock);
  init_microcode();
  interface_init(argc, argv);
  if(GIER_load("default.gier")==0)
  {
    fprintf(stderr, "Error loading default.gier; using default values.\n");
    GIER_init(30,True,NULL,NULL,NULL);
  }
  demomode=0;
  nimbi_board_poll();
  if(argc>1) GIERdemo(argv[1]);
  GIERmain();
  return 0;
}
