/*
	GIER interface (Windows 9x)

	(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 <math.h>
#include "GIER.h"
#include "common.h"

#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>

#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <time.h>

#include "gierres.h"

#ifdef LARGECONTROLBOARD
#include "kb1.h"
#include "kb2.h"
#else
#include "kb1_2.h"
#include "kb2_2.h"
#endif
#include "nimbi.h"
#include "typewriter.h"

#ifdef TYPEWRITER_SOUND
#include "typewriter_sound.h"
static int typewriter_sound_typers_count=0;
static int typewriter_sound_spaces_count=0;
static HWAVEOUT hwo;
static WAVEHDR *whdr;
#define BUFFERSIZE 50000
#define NBUFFERS 1
static char **sbuffers;
static int buffersize=0, nbuffers=0;
static int ibuffer,ipnt;
static void play_typewriter_sound(short *, int);
static void typewriter_check_paste();
#endif


#define ABS(x) ( (x) < 0 ? (-(x)) : (x) )
#define THISBUTTON(bx,by) ( ABS(MAKEPOINTS(lParam).x-bx)<10 && ABS(MAKEPOINTS(lParam).y-by)<10 )

static HWND mainhwnd=NULL, kb1hwnd=NULL, kb2hwnd=NULL, readerhwnd=NULL,
            punchhwnd=NULL, typewriterhwnd=NULL, printerhwnd=NULL,
	    plotterhwnd=NULL, demohwnd=NULL, nimbihwnd=NULL;
static HINSTANCE mainhInstance;
extern char *last_filename;

static int demo_textsize=24;
static int default_textsize=16;
static int small_textsize=7;
static int font_width, font_height;
static int demo_font_width, demo_font_height;
extern time_t start_time;

/* main */
static int debug_active=False;
static HFONT font, symbol_font, small_font, oldfont, demo_font;
static int newdrum, newbuffer;
static int newBDisk_present[MAXBDISKS], newBDisk_tracksize[MAXBDISKS], newBDisk_size[MAXBDISKS];
/* kb1 */
static int selected_register=0;
static Coord lowerzero[42], lowerone[42], select_register[16];
static COLORREF lampcolors[256];
/* kb2 */
static HWND select_out[5][5];
static int kb12_upper_count[44], kb12_lower_count[44];
static int kb12_aux_count[9];
static int kb12_count=0;
/* nimbi */
static int nimbi_count[12];
static COLORREF white_lampcolors[256];
/* printer */
int print_has_been_setup=0;
static int printer_pages=0, printer_lineno=0;
static HANDLE printer_hDevMode, printer_hDevNames;
static RECT printer_lastMargin;
static int printer_marginunits;
static int printer_lpi=6, printer_uadvance=1, printer_lines=65, printer_fontsize=11;
extern int is_drum_running();
extern unsigned char flx2a(char, int *);

/*  Printer buffer. We store the characters to be printed
    page-by-page until the user selects Print. */

typedef struct _PRINTERLINE
{
  struct _PRINTERLINE *nextline;
  unsigned char *oneline;
  int linelength;
} PRINTERLINE;

typedef struct _PRINTERPAGE
{
  struct _PRINTERPAGE *nextpage;
  struct _PRINTERLINE *firstline,*currentline;
  int firstcase,firstred; /* store case and red of first character; we need this if the user prints a
                    range of pages */
} PRINTERPAGE;

PRINTERPAGE *firstpage=NULL,*currentpage=NULL;

#define MAXPRINTERLINE 500
static unsigned char printerline[MAXPRINTERLINE];
static int printerlinelength=0;
static int printercase=0, printerred=0;

/* plotter */
int plotter_has_been_setup=0;
static int plotter_characters=0;
static HANDLE plotter_hDevMode, plotter_hDevNames;
static RECT plotter_lastMargin;
static int plotter_marginunits;
static int plotter_center=1, plotter_autoup=0, plotter_autodown=1,
           plotter_rotate=2, plotter_linewidth=4, plotter_linecolor=0;
static HWND plotter_linewidth_text, plotter_linewidth_edit;
static HWND plotter_linecolor_text, plotter_linecolor_edit;
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;

/* reader */
static int reader_current_fd = -1;
static int reader_modified = FALSE;
static char reader_filename[512]="";
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];
/* punch */
static int punch_current_fd = -1;
static char punch_char_table[PUNCH_TAPE_WINDOW];
static char punch_filename[512];
static int punch_filetype; /* 0: flx, 1: asc */
static int punch_uppercase=0;
/* typewriter */
static int typewriter_uppercase, typewriter_old_uppercase;
static short paper[LINEBUFFER][PAPERWIDTH][MAXOVERWRITE];
static int cur_row, cur_col;
static int redtext; /* 0: black, 1: red */
static char *typewriter_clipboard_input=NULL;
static int typewriter_clipboard_input_len=0, typewriter_clipboard_input_pnt=0;
static int scrollpos=0;
static int typewriter_nlines=19;
#define RED_MASK 256
#define SYMBOL_MASK 512
static int typewriter_waiting=0;
static update_viewmenu();
static void update_typewriter_waiting();
void checkdebug();
void interface_update();
void GIER_exit();
void kb1_update();
static void kb12_reset();
void kb12_update();
void nimbi_update();
static void kb1_getstate();
static void kb2_getstate();
static BOOL get_check(HWND);
static void set_check(HWND, BOOL);
void typewriter_SY(char);
void printer_SY(char);
void plotter_SY(char);
#define typewriter_CursorX(col) ( (col)*font_width )
#define typewriter_CursorY(row) ( ((row)+1)*font_height )
static void plotter_change_setup();
/* Progress bar */
#define MAXPROGRESSTITLE 80
static char progress_title[MAXPROGRESSTITLE];
static int progress_maxvalue;
static HWND progresshwnd=NULL, progressindicatorhwnd=NULL, progresslabelhwnd=NULL;
static int progress_lastvalue;
/* Demo */
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;
#define MAXDEMOTEXT 75
static char demotext[MAXDEMOTEXT];
void GIERdemocommand();
static void PrintMessage(UINT message)
{
  if(!(debug&DEBUGinterface)) return;
  if(message==0x0000) fprintf(debug_fh, "PrintMessage: WM_NULL\n");
  if(message==0x0001) fprintf(debug_fh, "PrintMessage: WM_CREATE\n");
  if(message==0x0002) fprintf(debug_fh, "PrintMessage: WM_DESTROY\n");
  if(message==0x0003) fprintf(debug_fh, "PrintMessage: WM_MOVE\n");
  if(message==0x0005) fprintf(debug_fh, "PrintMessage: WM_SIZE\n");
  if(message==0x0006) fprintf(debug_fh, "PrintMessage: WM_ACTIVATE\n");
  if(message==0x0007) fprintf(debug_fh, "PrintMessage: WM_SETFOCUS\n");
  if(message==0x0008) fprintf(debug_fh, "PrintMessage: WM_KILLFOCUS\n");
  if(message==0x000A) fprintf(debug_fh, "PrintMessage: WM_ENABLE\n");
  if(message==0x000B) fprintf(debug_fh, "PrintMessage: WM_SETREDRAW\n");
  if(message==0x000C) fprintf(debug_fh, "PrintMessage: WM_SETTEXT\n");
  if(message==0x000D) fprintf(debug_fh, "PrintMessage: WM_GETTEXT\n");
  if(message==0x000E) fprintf(debug_fh, "PrintMessage: WM_GETTEXTLENGTH\n");
  if(message==0x000F) fprintf(debug_fh, "PrintMessage: WM_PAINT\n");
  if(message==0x0010) fprintf(debug_fh, "PrintMessage: WM_CLOSE\n");
  if(message==0x0011) fprintf(debug_fh, "PrintMessage: WM_QUERYENDSESSION\n");
  if(message==0x0013) fprintf(debug_fh, "PrintMessage: WM_QUERYOPEN\n");
  if(message==0x0016) fprintf(debug_fh, "PrintMessage: WM_ENDSESSION\n");
  if(message==0x0012) fprintf(debug_fh, "PrintMessage: WM_QUIT\n");
  if(message==0x0014) fprintf(debug_fh, "PrintMessage: WM_ERASEBKGND\n");
  if(message==0x0015) fprintf(debug_fh, "PrintMessage: WM_SYSCOLORCHANGE\n");
  if(message==0x0018) fprintf(debug_fh, "PrintMessage: WM_SHOWWINDOW\n");
  if(message==0x001A) fprintf(debug_fh, "PrintMessage: WM_WININICHANGE\n");
  if(message==0x001B) fprintf(debug_fh, "PrintMessage: WM_DEVMODECHANGE\n");
  if(message==0x001C) fprintf(debug_fh, "PrintMessage: WM_ACTIVATEAPP\n");
  if(message==0x001D) fprintf(debug_fh, "PrintMessage: WM_FONTCHANGE\n");
  if(message==0x001E) fprintf(debug_fh, "PrintMessage: WM_TIMECHANGE\n");
  if(message==0x001F) fprintf(debug_fh, "PrintMessage: WM_CANCELMODE\n");
  if(message==0x0020) fprintf(debug_fh, "PrintMessage: WM_SETCURSOR\n");
  if(message==0x0021) fprintf(debug_fh, "PrintMessage: WM_MOUSEACTIVATE\n");
  if(message==0x0022) fprintf(debug_fh, "PrintMessage: WM_CHILDACTIVATE\n");
  if(message==0x0023) fprintf(debug_fh, "PrintMessage: WM_QUEUESYNC\n");
  if(message==0x0024) fprintf(debug_fh, "PrintMessage: WM_GETMINMAXINFO\n");
  if(message==0x0026) fprintf(debug_fh, "PrintMessage: WM_PAINTICON\n");
  if(message==0x0027) fprintf(debug_fh, "PrintMessage: WM_ICONERASEBKGND\n");
  if(message==0x0028) fprintf(debug_fh, "PrintMessage: WM_NEXTDLGCTL\n");
  if(message==0x002A) fprintf(debug_fh, "PrintMessage: WM_SPOOLERSTATUS\n");
  if(message==0x002B) fprintf(debug_fh, "PrintMessage: WM_DRAWITEM\n");
  if(message==0x002C) fprintf(debug_fh, "PrintMessage: WM_MEASUREITEM\n");
  if(message==0x002D) fprintf(debug_fh, "PrintMessage: WM_DELETEITEM\n");
  if(message==0x002E) fprintf(debug_fh, "PrintMessage: WM_VKEYTOITEM\n");
  if(message==0x002F) fprintf(debug_fh, "PrintMessage: WM_CHARTOITEM\n");
  if(message==0x0030) fprintf(debug_fh, "PrintMessage: WM_SETFONT\n");
  if(message==0x0031) fprintf(debug_fh, "PrintMessage: WM_GETFONT\n");
  if(message==0x0032) fprintf(debug_fh, "PrintMessage: WM_SETHOTKEY\n");
  if(message==0x0033) fprintf(debug_fh, "PrintMessage: WM_GETHOTKEY\n");
  if(message==0x0037) fprintf(debug_fh, "PrintMessage: WM_QUERYDRAGICON\n");
  if(message==0x0039) fprintf(debug_fh, "PrintMessage: WM_COMPAREITEM\n");
  if(message==0x003D) fprintf(debug_fh, "PrintMessage: WM_GETOBJECT\n");
  if(message==0x0041) fprintf(debug_fh, "PrintMessage: WM_COMPACTING\n");
  if(message==0x0044) fprintf(debug_fh, "PrintMessage: WM_COMMNOTIFY\n");
  if(message==0x0046) fprintf(debug_fh, "PrintMessage: WM_WINDOWPOSCHANGING\n");
  if(message==0x0047) fprintf(debug_fh, "PrintMessage: WM_WINDOWPOSCHANGED\n");
  if(message==0x0048) fprintf(debug_fh, "PrintMessage: WM_POWER\n");
  if(message==0x004A) fprintf(debug_fh, "PrintMessage: WM_COPYDATA\n");
  if(message==0x004B) fprintf(debug_fh, "PrintMessage: WM_CANCELJOURNAL\n");
  if(message==0x004E) fprintf(debug_fh, "PrintMessage: WM_NOTIFY\n");
  if(message==0x0050) fprintf(debug_fh, "PrintMessage: WM_INPUTLANGCHANGEREQUEST\n");
  if(message==0x0051) fprintf(debug_fh, "PrintMessage: WM_INPUTLANGCHANGE\n");
  if(message==0x0052) fprintf(debug_fh, "PrintMessage: WM_TCARD\n");
  if(message==0x0053) fprintf(debug_fh, "PrintMessage: WM_HELP\n");
  if(message==0x0054) fprintf(debug_fh, "PrintMessage: WM_USERCHANGED\n");
  if(message==0x0055) fprintf(debug_fh, "PrintMessage: WM_NOTIFYFORMAT\n");
  if(message==0x007B) fprintf(debug_fh, "PrintMessage: WM_CONTEXTMENU\n");
  if(message==0x007C) fprintf(debug_fh, "PrintMessage: WM_STYLECHANGING\n");
  if(message==0x007D) fprintf(debug_fh, "PrintMessage: WM_STYLECHANGED\n");
  if(message==0x007E) fprintf(debug_fh, "PrintMessage: WM_DISPLAYCHANGE\n");
  if(message==0x007F) fprintf(debug_fh, "PrintMessage: WM_GETICON\n");
  if(message==0x0080) fprintf(debug_fh, "PrintMessage: WM_SETICON\n");
  if(message==0x0081) fprintf(debug_fh, "PrintMessage: WM_NCCREATE\n");
  if(message==0x0082) fprintf(debug_fh, "PrintMessage: WM_NCDESTROY\n");
  if(message==0x0083) fprintf(debug_fh, "PrintMessage: WM_NCCALCSIZE\n");
  if(message==0x0084) fprintf(debug_fh, "PrintMessage: WM_NCHITTEST\n");
  if(message==0x0085) fprintf(debug_fh, "PrintMessage: WM_NCPAINT\n");
  if(message==0x0086) fprintf(debug_fh, "PrintMessage: WM_NCACTIVATE\n");
  if(message==0x0087) fprintf(debug_fh, "PrintMessage: WM_GETDLGCODE\n");
  if(message==0x0088) fprintf(debug_fh, "PrintMessage: WM_SYNCPAINT\n");
  if(message==0x00A0) fprintf(debug_fh, "PrintMessage: WM_NCMOUSEMOVE\n");
  if(message==0x00A1) fprintf(debug_fh, "PrintMessage: WM_NCLBUTTONDOWN\n");
  if(message==0x00A2) fprintf(debug_fh, "PrintMessage: WM_NCLBUTTONUP\n");
  if(message==0x00A3) fprintf(debug_fh, "PrintMessage: WM_NCLBUTTONDBLCLK\n");
  if(message==0x00A4) fprintf(debug_fh, "PrintMessage: WM_NCRBUTTONDOWN\n");
  if(message==0x00A5) fprintf(debug_fh, "PrintMessage: WM_NCRBUTTONUP\n");
  if(message==0x00A6) fprintf(debug_fh, "PrintMessage: WM_NCRBUTTONDBLCLK\n");
  if(message==0x00A7) fprintf(debug_fh, "PrintMessage: WM_NCMBUTTONDOWN\n");
  if(message==0x00A8) fprintf(debug_fh, "PrintMessage: WM_NCMBUTTONUP\n");
  if(message==0x00A9) fprintf(debug_fh, "PrintMessage: WM_NCMBUTTONDBLCLK\n");
  if(message==0x00AB) fprintf(debug_fh, "PrintMessage: WM_NCXBUTTONDOWN\n");
  if(message==0x00AC) fprintf(debug_fh, "PrintMessage: WM_NCXBUTTONUP\n");
  if(message==0x00AD) fprintf(debug_fh, "PrintMessage: WM_NCXBUTTONDBLCLK\n");
  if(message==0x00fe) fprintf(debug_fh, "PrintMessage: WM_INPUT_DEVICE_CHANGE\n");
  if(message==0x00FF) fprintf(debug_fh, "PrintMessage: WM_INPUT\n");
  if(message==0x0100) fprintf(debug_fh, "PrintMessage: WM_KEYFIRST\n");
  if(message==0x0100) fprintf(debug_fh, "PrintMessage: WM_KEYDOWN\n");
  if(message==0x0101) fprintf(debug_fh, "PrintMessage: WM_KEYUP\n");
  if(message==0x0102) fprintf(debug_fh, "PrintMessage: WM_CHAR\n");
  if(message==0x0103) fprintf(debug_fh, "PrintMessage: WM_DEADCHAR\n");
  if(message==0x0104) fprintf(debug_fh, "PrintMessage: WM_SYSKEYDOWN\n");
  if(message==0x0105) fprintf(debug_fh, "PrintMessage: WM_SYSKEYUP\n");
  if(message==0x0106) fprintf(debug_fh, "PrintMessage: WM_SYSCHAR\n");
  if(message==0x0107) fprintf(debug_fh, "PrintMessage: WM_SYSDEADCHAR\n");
  if(message==0x0109) fprintf(debug_fh, "PrintMessage: WM_UNICHAR\n");
  if(message==0x0109) fprintf(debug_fh, "PrintMessage: WM_KEYLAST\n");
  if(message==0x010D) fprintf(debug_fh, "PrintMessage: WM_IME_STARTCOMPOSITION\n");
  if(message==0x010E) fprintf(debug_fh, "PrintMessage: WM_IME_ENDCOMPOSITION\n");
  if(message==0x010F) fprintf(debug_fh, "PrintMessage: WM_IME_COMPOSITION\n");
  if(message==0x010F) fprintf(debug_fh, "PrintMessage: WM_IME_KEYLAST\n");
  if(message==0x0110) fprintf(debug_fh, "PrintMessage: WM_INITDIALOG\n");
  if(message==0x0111) fprintf(debug_fh, "PrintMessage: WM_COMMAND\n");
  if(message==0x0112) fprintf(debug_fh, "PrintMessage: WM_SYSCOMMAND\n");
  if(message==0x0113) fprintf(debug_fh, "PrintMessage: WM_TIMER\n");
  if(message==0x0114) fprintf(debug_fh, "PrintMessage: WM_HSCROLL\n");
  if(message==0x0115) fprintf(debug_fh, "PrintMessage: WM_VSCROLL\n");
  if(message==0x0116) fprintf(debug_fh, "PrintMessage: WM_INITMENU\n");
  if(message==0x0117) fprintf(debug_fh, "PrintMessage: WM_INITMENUPOPUP\n");
  if(message==0x011F) fprintf(debug_fh, "PrintMessage: WM_MENUSELECT\n");
  if(message==0x0119) fprintf(debug_fh, "PrintMessage: WM_GESTURE\n");
  if(message==0x011A) fprintf(debug_fh, "PrintMessage: WM_GESTURENOTIFY\n");
  if(message==0x0120) fprintf(debug_fh, "PrintMessage: WM_MENUCHAR\n");
  if(message==0x0121) fprintf(debug_fh, "PrintMessage: WM_ENTERIDLE\n");
  if(message==0x0122) fprintf(debug_fh, "PrintMessage: WM_MENURBUTTONUP\n");
  if(message==0x0123) fprintf(debug_fh, "PrintMessage: WM_MENUDRAG\n");
  if(message==0x0124) fprintf(debug_fh, "PrintMessage: WM_MENUGETOBJECT\n");
  if(message==0x0125) fprintf(debug_fh, "PrintMessage: WM_UNINITMENUPOPUP\n");
  if(message==0x0126) fprintf(debug_fh, "PrintMessage: WM_MENUCOMMAND\n");
  if(message==0x0127) fprintf(debug_fh, "PrintMessage: WM_CHANGEUISTATE\n");
  if(message==0x0128) fprintf(debug_fh, "PrintMessage: WM_UPDATEUISTATE\n");
  if(message==0x0129) fprintf(debug_fh, "PrintMessage: WM_QUERYUISTATE\n");
  if(message==0x0132) fprintf(debug_fh, "PrintMessage: WM_CTLCOLORMSGBOX\n");
  if(message==0x0133) fprintf(debug_fh, "PrintMessage: WM_CTLCOLOREDIT\n");
  if(message==0x0134) fprintf(debug_fh, "PrintMessage: WM_CTLCOLORLISTBOX\n");
  if(message==0x0135) fprintf(debug_fh, "PrintMessage: WM_CTLCOLORBTN\n");
  if(message==0x0136) fprintf(debug_fh, "PrintMessage: WM_CTLCOLORDLG\n");
  if(message==0x0137) fprintf(debug_fh, "PrintMessage: WM_CTLCOLORSCROLLBAR\n");
  if(message==0x0138) fprintf(debug_fh, "PrintMessage: WM_CTLCOLORSTATIC\n");
  if(message==0x0200) fprintf(debug_fh, "PrintMessage: WM_MOUSEFIRST\n");
  if(message==0x0200) fprintf(debug_fh, "PrintMessage: WM_MOUSEMOVE\n");
  if(message==0x0201) fprintf(debug_fh, "PrintMessage: WM_LBUTTONDOWN\n");
  if(message==0x0202) fprintf(debug_fh, "PrintMessage: WM_LBUTTONUP\n");
  if(message==0x0203) fprintf(debug_fh, "PrintMessage: WM_LBUTTONDBLCLK\n");
  if(message==0x0204) fprintf(debug_fh, "PrintMessage: WM_RBUTTONDOWN\n");
  if(message==0x0205) fprintf(debug_fh, "PrintMessage: WM_RBUTTONUP\n");
  if(message==0x0206) fprintf(debug_fh, "PrintMessage: WM_RBUTTONDBLCLK\n");
  if(message==0x0207) fprintf(debug_fh, "PrintMessage: WM_MBUTTONDOWN\n");
  if(message==0x0208) fprintf(debug_fh, "PrintMessage: WM_MBUTTONUP\n");
  if(message==0x0209) fprintf(debug_fh, "PrintMessage: WM_MBUTTONDBLCLK\n");
  if(message==0x020A) fprintf(debug_fh, "PrintMessage: WM_MOUSEWHEEL\n");
  if(message==0x020B) fprintf(debug_fh, "PrintMessage: WM_XBUTTONDOWN\n");
  if(message==0x020C) fprintf(debug_fh, "PrintMessage: WM_XBUTTONUP\n");
  if(message==0x020D) fprintf(debug_fh, "PrintMessage: WM_XBUTTONDBLCLK\n");
  if(message==0x020e) fprintf(debug_fh, "PrintMessage: WM_MOUSEHWHEEL\n");
  if(message==0x020e) fprintf(debug_fh, "PrintMessage: WM_MOUSELAST\n");
  if(message==0x020d) fprintf(debug_fh, "PrintMessage: WM_MOUSELAST\n");
  if(message==0x0210) fprintf(debug_fh, "PrintMessage: WM_PARENTNOTIFY\n");
  if(message==0x0211) fprintf(debug_fh, "PrintMessage: WM_ENTERMENULOOP\n");
  if(message==0x0212) fprintf(debug_fh, "PrintMessage: WM_EXITMENULOOP\n");
  if(message==0x0213) fprintf(debug_fh, "PrintMessage: WM_NEXTMENU\n");
  if(message==0x0214) fprintf(debug_fh, "PrintMessage: WM_SIZING\n");
  if(message==0x0215) fprintf(debug_fh, "PrintMessage: WM_CAPTURECHANGED\n");
  if(message==0x0216) fprintf(debug_fh, "PrintMessage: WM_MOVING\n");
  if(message==0x0218) fprintf(debug_fh, "PrintMessage: WM_POWERBROADCAST\n");
  if(message==0x0219) fprintf(debug_fh, "PrintMessage: WM_DEVICECHANGE\n");
  if(message==0x0220) fprintf(debug_fh, "PrintMessage: WM_MDICREATE\n");
  if(message==0x0221) fprintf(debug_fh, "PrintMessage: WM_MDIDESTROY\n");
  if(message==0x0222) fprintf(debug_fh, "PrintMessage: WM_MDIACTIVATE\n");
  if(message==0x0223) fprintf(debug_fh, "PrintMessage: WM_MDIRESTORE\n");
  if(message==0x0224) fprintf(debug_fh, "PrintMessage: WM_MDINEXT\n");
  if(message==0x0225) fprintf(debug_fh, "PrintMessage: WM_MDIMAXIMIZE\n");
  if(message==0x0226) fprintf(debug_fh, "PrintMessage: WM_MDITILE\n");
  if(message==0x0227) fprintf(debug_fh, "PrintMessage: WM_MDICASCADE\n");
  if(message==0x0228) fprintf(debug_fh, "PrintMessage: WM_MDIICONARRANGE\n");
  if(message==0x0229) fprintf(debug_fh, "PrintMessage: WM_MDIGETACTIVE\n");
  if(message==0x0230) fprintf(debug_fh, "PrintMessage: WM_MDISETMENU\n");
  if(message==0x0231) fprintf(debug_fh, "PrintMessage: WM_ENTERSIZEMOVE\n");
  if(message==0x0232) fprintf(debug_fh, "PrintMessage: WM_EXITSIZEMOVE\n");
  if(message==0x0233) fprintf(debug_fh, "PrintMessage: WM_DROPFILES\n");
  if(message==0x0234) fprintf(debug_fh, "PrintMessage: WM_MDIREFRESHMENU\n");
  if(message==0x238) fprintf(debug_fh, "PrintMessage: WM_POINTERDEVICECHANGE\n");
  if(message==0x239) fprintf(debug_fh, "PrintMessage: WM_POINTERDEVICEINRANGE\n");
  if(message==0x23a) fprintf(debug_fh, "PrintMessage: WM_POINTERDEVICEOUTOFRANGE\n");
  if(message==0x0240) fprintf(debug_fh, "PrintMessage: WM_TOUCH\n");
  if(message==0x0241) fprintf(debug_fh, "PrintMessage: WM_NCPOINTERUPDATE\n");
  if(message==0x0242) fprintf(debug_fh, "PrintMessage: WM_NCPOINTERDOWN\n");
  if(message==0x0243) fprintf(debug_fh, "PrintMessage: WM_NCPOINTERUP\n");
  if(message==0x0245) fprintf(debug_fh, "PrintMessage: WM_POINTERUPDATE\n");
  if(message==0x0246) fprintf(debug_fh, "PrintMessage: WM_POINTERDOWN\n");
  if(message==0x0247) fprintf(debug_fh, "PrintMessage: WM_POINTERUP\n");
  if(message==0x0249) fprintf(debug_fh, "PrintMessage: WM_POINTERENTER\n");
  if(message==0x024a) fprintf(debug_fh, "PrintMessage: WM_POINTERLEAVE\n");
  if(message==0x024b) fprintf(debug_fh, "PrintMessage: WM_POINTERACTIVATE\n");
  if(message==0x024c) fprintf(debug_fh, "PrintMessage: WM_POINTERCAPTURECHANGED\n");
  if(message==0x024d) fprintf(debug_fh, "PrintMessage: WM_TOUCHHITTESTING\n");
  if(message==0x024e) fprintf(debug_fh, "PrintMessage: WM_POINTERWHEEL\n");
  if(message==0x024f) fprintf(debug_fh, "PrintMessage: WM_POINTERHWHEEL\n");
  if(message==0x0281) fprintf(debug_fh, "PrintMessage: WM_IME_SETCONTEXT\n");
  if(message==0x0282) fprintf(debug_fh, "PrintMessage: WM_IME_NOTIFY\n");
  if(message==0x0283) fprintf(debug_fh, "PrintMessage: WM_IME_CONTROL\n");
  if(message==0x0284) fprintf(debug_fh, "PrintMessage: WM_IME_COMPOSITIONFULL\n");
  if(message==0x0285) fprintf(debug_fh, "PrintMessage: WM_IME_SELECT\n");
  if(message==0x0286) fprintf(debug_fh, "PrintMessage: WM_IME_CHAR\n");
  if(message==0x0288) fprintf(debug_fh, "PrintMessage: WM_IME_REQUEST\n");
  if(message==0x0290) fprintf(debug_fh, "PrintMessage: WM_IME_KEYDOWN\n");
  if(message==0x0291) fprintf(debug_fh, "PrintMessage: WM_IME_KEYUP\n");
  if(message==0x02A1) fprintf(debug_fh, "PrintMessage: WM_MOUSEHOVER\n");
  if(message==0x02A3) fprintf(debug_fh, "PrintMessage: WM_MOUSELEAVE\n");
  if(message==0x02A0) fprintf(debug_fh, "PrintMessage: WM_NCMOUSEHOVER\n");
  if(message==0x02A2) fprintf(debug_fh, "PrintMessage: WM_NCMOUSELEAVE\n");
  if(message==0x02B1) fprintf(debug_fh, "PrintMessage: WM_WTSSESSION_CHANGE\n");
  if(message==0x02c0) fprintf(debug_fh, "PrintMessage: WM_TABLET_FIRST\n");
  if(message==0x02df) fprintf(debug_fh, "PrintMessage: WM_TABLET_LAST\n");
  if(message==0x0300) fprintf(debug_fh, "PrintMessage: WM_CUT\n");
  if(message==0x0301) fprintf(debug_fh, "PrintMessage: WM_COPY\n");
  if(message==0x0302) fprintf(debug_fh, "PrintMessage: WM_PASTE\n");
  if(message==0x0303) fprintf(debug_fh, "PrintMessage: WM_CLEAR\n");
  if(message==0x0304) fprintf(debug_fh, "PrintMessage: WM_UNDO\n");
  if(message==0x0305) fprintf(debug_fh, "PrintMessage: WM_RENDERFORMAT\n");
  if(message==0x0306) fprintf(debug_fh, "PrintMessage: WM_RENDERALLFORMATS\n");
  if(message==0x0307) fprintf(debug_fh, "PrintMessage: WM_DESTROYCLIPBOARD\n");
  if(message==0x0308) fprintf(debug_fh, "PrintMessage: WM_DRAWCLIPBOARD\n");
  if(message==0x0309) fprintf(debug_fh, "PrintMessage: WM_PAINTCLIPBOARD\n");
  if(message==0x030A) fprintf(debug_fh, "PrintMessage: WM_VSCROLLCLIPBOARD\n");
  if(message==0x030B) fprintf(debug_fh, "PrintMessage: WM_SIZECLIPBOARD\n");
  if(message==0x030C) fprintf(debug_fh, "PrintMessage: WM_ASKCBFORMATNAME\n");
  if(message==0x030D) fprintf(debug_fh, "PrintMessage: WM_CHANGECBCHAIN\n");
  if(message==0x030E) fprintf(debug_fh, "PrintMessage: WM_HSCROLLCLIPBOARD\n");
  if(message==0x030F) fprintf(debug_fh, "PrintMessage: WM_QUERYNEWPALETTE\n");
  if(message==0x0310) fprintf(debug_fh, "PrintMessage: WM_PALETTEISCHANGING\n");
  if(message==0x0311) fprintf(debug_fh, "PrintMessage: WM_PALETTECHANGED\n");
  if(message==0x0312) fprintf(debug_fh, "PrintMessage: WM_HOTKEY\n");
  if(message==0x0317) fprintf(debug_fh, "PrintMessage: WM_PRINT\n");
  if(message==0x0318) fprintf(debug_fh, "PrintMessage: WM_PRINTCLIENT\n");
  if(message==0x0319) fprintf(debug_fh, "PrintMessage: WM_APPCOMMAND\n");
  if(message==0x031A) fprintf(debug_fh, "PrintMessage: WM_THEMECHANGED\n");
  if(message==0x031d) fprintf(debug_fh, "PrintMessage: WM_CLIPBOARDUPDATE\n");
  if(message==0x031e) fprintf(debug_fh, "PrintMessage: WM_DWMCOMPOSITIONCHANGED\n");
  if(message==0x031f) fprintf(debug_fh, "PrintMessage: WM_DWMNCRENDERINGCHANGED\n");
  if(message==0x0320) fprintf(debug_fh, "PrintMessage: WM_DWMCOLORIZATIONCOLORCHANGED\n");
  if(message==0x0321) fprintf(debug_fh, "PrintMessage: WM_DWMWINDOWMAXIMIZEDCHANGE\n");
  if(message==0x0323) fprintf(debug_fh, "PrintMessage: WM_DWMSENDICONICTHUMBNAIL\n");
  if(message==0x0326) fprintf(debug_fh, "PrintMessage: WM_DWMSENDICONICLIVEPREVIEWBITMAP\n");
  if(message==0x033f) fprintf(debug_fh, "PrintMessage: WM_GETTITLEBARINFOEX\n");
  if(message==0x0358) fprintf(debug_fh, "PrintMessage: WM_HANDHELDFIRST\n");
  if(message==0x035F) fprintf(debug_fh, "PrintMessage: WM_HANDHELDLAST\n");
  if(message==0x0360) fprintf(debug_fh, "PrintMessage: WM_AFXFIRST\n");
  if(message==0x037F) fprintf(debug_fh, "PrintMessage: WM_AFXLAST\n");
  if(message==0x0380) fprintf(debug_fh, "PrintMessage: WM_PENWINFIRST\n");
  if(message==0x038F) fprintf(debug_fh, "PrintMessage: WM_PENWINLAST\n");
  if(message==0x8000) fprintf(debug_fh, "PrintMessage: WM_APP\n");
  if(message==0x0400) fprintf(debug_fh, "PrintMessage: WM_USER\n");
}

#undef IN

LRESULT CALLBACK progressWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;

  PAINTSTRUCT ps;

  switch(message)
  {
  case WM_PAINT:
    hdc = BeginPaint(hWnd, &ps);
    EndPaint(hWnd, &ps);
    break;
  case WM_DESTROY:
    progresshwnd = NULL;
    break;
  default:
    return(DefWindowProc(hWnd, message, wParam, lParam));
  }
  return 0;
}

static BOOL progressInitApplication(HINSTANCE hInstance)
{
  WNDCLASS wc;
  BOOL rc;

  wc.style = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc = progressWndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = NULL;
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
  wc.lpszMenuName = NULL;
  wc.lpszClassName = "GIERprogress";

  rc=RegisterClass(&wc);
  return rc;
}

static BOOL progressInitInstance(HINSTANCE hinstance, INT ncmdshow)
{
  RECT warea;

  warea.left = 0;
  warea.right = 250;
  warea.top = 0;
  warea.bottom = 100;
  AdjustWindowRect(&warea, WS_OVERLAPPEDWINDOW, False);

  progresshwnd = CreateWindow("GIERprogress", "GIER Progress indicator", WS_OVERLAPPEDWINDOW,
         GetSystemMetrics(SM_CXSCREEN)/2-250/2, 
	 GetSystemMetrics(SM_CYSCREEN)/2-100/2, warea.right-warea.left+1, warea.bottom-warea.top+1,
	 (HWND)NULL,(HMENU)NULL, hinstance, (LPSTR)NULL);

  if(progresshwnd == NULL) return 0;

  ShowWindow(progresshwnd, ncmdshow);
  UpdateWindow(progresshwnd);
}

int progress_init(int maxvalue, char *title)
{
  progressInitApplication(mainhInstance);
  progressInitInstance(mainhInstance, SW_SHOWNORMAL);
  progressindicatorhwnd = CreateWindowEx(0, "msctls_progress32", NULL, WS_CHILD|WS_VISIBLE|PBS_SMOOTH,
      25, 56, 200, 20, progresshwnd, (HMENU) NULL, mainhInstance, (LPSTR)NULL);
  SendMessage(progressindicatorhwnd, PBM_SETRANGE, 0, MAKELPARAM(0,100));
  SendMessage(progressindicatorhwnd, PBM_SETPOS, (WPARAM) 0, 0);
  ShowWindow(progressindicatorhwnd, SW_SHOWNORMAL);
  progresslabelhwnd = CreateWindow("STATIC", title, WS_CHILD|WS_GROUP|WS_VISIBLE,
		   50, 18, 140, 30,
		   progresshwnd, (HMENU)NULL, mainhInstance, (LPSTR)NULL);
  UpdateWindow(progresshwnd);
  progress_maxvalue=maxvalue;
  progress_lastvalue=-1000;
}

int progress_setvalue(int value)
{
  int thisvalue;
  thisvalue=100.0*value/progress_maxvalue;
  if(thisvalue>=progress_lastvalue+5)
  {
    SendMessage(progressindicatorhwnd, PBM_SETPOS, (WPARAM) (100*value/progress_maxvalue), 0);
    UpdateWindow(progressindicatorhwnd);
    progress_lastvalue=thisvalue;
  }
}

int progress_stop()
{
  if(progresshwnd==NULL) return;
  DestroyWindow(progresshwnd);
}

BOOL confirm(HWND hwnd, char *txt)
{
  return 1;
}

interface_init()
{
}

interface_check_running()
{
}

GIER_start()
{
  running = 1;
}

GIER_stop()
{
  running = 0;
}

static void kb2_draw_bit(HDC hdc, int y, int status)
{
  HPEN hpen, hpenold;
  hpen = CreatePen(PS_SOLID, 2, lampcolors[status*255/kb12_count]);
  hpenold = SelectObject(hdc, hpen);
  MoveToEx(hdc, kb2_low_x1, y, NULL);
  LineTo(hdc, kb2_low_x2, y);
  SelectObject(hdc, hpenold);
  DeleteObject(hpen);
}

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;
}

void kb2_update()
{
  HDC hdc;
  HBRUSH hbr, hbrold;

  if(debug&DEBUGinterface) fprintf(debug_fh, "kb2_update called\n");
  if(kb2hwnd == NULL) return;
  if(kb12_count==0) kb12_count=1;

  hdc = GetDC(kb2hwnd);
  hbr = GetStockObject(BLACK_BRUSH);
  hbrold = SelectObject(hdc, hbr);
  DeleteObject(hbrold);
  kb2_draw_bit(hdc, kb2_KA_y, kb12_aux_count[0]);
  kb2_draw_bit(hdc, kb2_KB_y, kb12_aux_count[1]);
  kb2_draw_bit(hdc, Klar_y, kb12_aux_count[2]);
  kb2_draw_bit(hdc, YE_y, kb12_aux_count[3]);
  kb2_draw_bit(hdc, Tape_Parity_y, kb12_aux_count[4]);
  kb2_draw_bit(hdc, Drum_Parity_y, kb12_aux_count[5]);
  kb2_draw_bit(hdc, TO_y, kb12_aux_count[6]);
  kb2_draw_bit(hdc, iloop_y, kb12_aux_count[7]);
  kb2_draw_bit(hdc, HP_inhib_y, kb12_aux_count[8]);
  DeleteObject(hbr);
  ReleaseDC(kb2hwnd, hdc);
}

LRESULT CALLBACK kb2WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;
  HDC hdcBitmap;
  HBITMAP hbmp, hbmpOld;
  BITMAP Bitmap_Info;
  LONG bottom, right;
  int changed=0;
  GIERword lower,mask;
  int i,j;

  PAINTSTRUCT ps;

  switch(message)
  {
  case WM_COMMAND:
    if(debug&DEBUGinterface) fprintf(debug_fh, "kb2 WM_COMMAND. notifycode: %d, id: %d, wndCtl: %ld\n", HIWORD(wParam), LOWORD(wParam), lParam);
    for(i=1; i<5; i++)
    for(j=1; j<5; j++)
    {
      if(lParam == (LPARAM) (select_out[i][j]))
      {
        selected_out[i] &= ~(1<<(j+2));
	if(get_check(select_out[i][j])) selected_out[i] |= 1<<(j+2);
        if(debug&DEBUGinterface) fprintf(debug_fh, "selected_out[%d] = %d\n", i, selected_out[i]);
        break;
      }
    }
    break;
  case WM_PAINT:
    hdc = BeginPaint(hWnd, &ps);
    if(debug&DEBUGinterface)
    {
      fprintf(debug_fh, "kb2 WM_PAINT.\n");
      fprintf(debug_fh, "left: %d, right: %d, top: %d, bottom: %d\n",
             ps.rcPaint.left, ps.rcPaint.right, ps.rcPaint.top, ps.rcPaint.bottom);
    }
    hbmp = LoadBitmap(mainhInstance, "kb2_bitmap");
    GetObject(hbmp, sizeof(Bitmap_Info), &Bitmap_Info);
    hdcBitmap = CreateCompatibleDC(hdc);
    hbmpOld = SelectObject(hdcBitmap, hbmp);
    if(debug&DEBUGinterface) fprintf(debug_fh, "kb2 bitmap. width: %d height: %d\n", Bitmap_Info.bmWidth, Bitmap_Info.bmHeight);
    right = ps.rcPaint.right;
    if(right >= Bitmap_Info.bmWidth) right=Bitmap_Info.bmWidth-1;
    bottom = ps.rcPaint.bottom;
    if(bottom >= Bitmap_Info.bmHeight) bottom=Bitmap_Info.bmHeight-1;
    BitBlt(hdc, ps.rcPaint.left, ps.rcPaint.top, right-ps.rcPaint.left+1, bottom-ps.rcPaint.top+1,
           hdcBitmap, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
    if(debug&DEBUGinterface) fprintf(debug_fh, "bitblt: %d %d %d %d < %d %d\n",
      ps.rcPaint.left, ps.rcPaint.top, right-ps.rcPaint.left+1, bottom-ps.rcPaint.top+1,
      ps.rcPaint.left, ps.rcPaint.top);
    SelectObject(hdcBitmap, hbmpOld);
    DeleteDC(hdcBitmap);
    DeleteObject(hbmp);
    DeleteObject(hbmpOld);
    EndPaint(hWnd, &ps);
    kb2_update();
    break;
  case WM_LBUTTONDOWN:
    if(debug&DEBUGinterface) fprintf(debug_fh, "x: %d y: %d\n", MAKEPOINTS(lParam).x, MAKEPOINTS(lParam).y);
    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();
    }
    if(changed)
    {
      kb12_reset();
      kb12_update();
      kb1_update();
      kb2_update();
      nimbi_update();
      kb12_reset();
    }
    break;
  case WM_RBUTTONDOWN:
    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();
    }
    break;
  case WM_DESTROY:
    kb2hwnd = NULL;
    update_viewmenu();
    break;
  default:
    return(DefWindowProc(hWnd, message, wParam, lParam));
  }
  return 0;
}

static BOOL kb2InitApplication(HINSTANCE hInstance)
{
  WNDCLASS wc;
  BOOL rc;

  wc.style = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc = kb2WndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = NULL;
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
  wc.lpszMenuName = NULL;
  wc.lpszClassName = "GIERkb2";

  rc=RegisterClass(&wc);
  return rc;
}

static BOOL kb2InitInstance(HINSTANCE hinstance, INT ncmdshow)
{
  RECT warea;

  warea.left = 0;
  warea.right = kb2_width-1;
  warea.top = 0;
  warea.bottom = kb2_height-1+100;
  AdjustWindowRect(&warea, WS_OVERLAPPEDWINDOW, False);
  /* 560, 80 */
  kb2hwnd = CreateWindow("GIERkb2", "GIER Aux. Control Board", WS_OVERLAPPEDWINDOW,
         740, 390, warea.right-warea.left+1, warea.bottom-warea.top+1,
	 (HWND)NULL,(HMENU)NULL, hinstance, (LPSTR)NULL);

  if(kb2hwnd == NULL) return 0;

  ShowWindow(kb2hwnd, ncmdshow);
  UpdateWindow(kb2hwnd);

  return 1;
}

static BOOL get_check(HWND hwnd)
{
  return SendMessage(hwnd, BM_GETCHECK, 0, 0)==BST_CHECKED;
}

static void set_check(HWND hwnd, BOOL state)
{
  SendMessage(hwnd, BM_SETCHECK, state?BST_CHECKED:BST_UNCHECKED, 0);
}

static void kb2_selected_create()
{
  int i,j;
  char *labels1[] = {"","8","16","32","64"};
  char *labels2[] = {"","Printer","Typewriter","Punch","Plotter"};
#define KB2_SELECTED_X(i,j) (50+(j)*30)
#ifdef LARGECONTROLBOARD
#define KB2_SELECTED_Y(i,j) (292+((i)*16))
#else
#define KB2_SELECTED_Y(i,j) (150+((i)*16))
#endif
  if(debug&DEBUGinterface) fprintf(debug_fh, "kb2_selected_create called\n");
  for(i=1; i<5; i++)
  for(j=1; j<5; j++)
  {
    select_out[i][j] = CreateWindow("BUTTON", "", WS_CHILD|WS_GROUP|WS_VISIBLE|BS_AUTOCHECKBOX,
                     KB2_SELECTED_X(i,j), KB2_SELECTED_Y(i,j), 16, 16,
		     kb2hwnd, (HMENU)NULL, mainhInstance, (LPSTR)NULL);
  }
  for(j=1; j<5; j++)
  {
    select_out[0][j] = CreateWindow("STATIC", labels1[j], WS_CHILD|WS_GROUP|WS_VISIBLE,
                     KB2_SELECTED_X(0,j), KB2_SELECTED_Y(0,j), 30, 16,
		     kb2hwnd, (HMENU)NULL, mainhInstance, (LPSTR)NULL);
  }
  for(i=1; i<5; i++)
  {
    select_out[i][0] = CreateWindow("STATIC", labels2[i], WS_CHILD|WS_GROUP|WS_VISIBLE,
                     0, KB2_SELECTED_Y(i,0), 80, 16,
		     kb2hwnd, (HMENU)NULL, mainhInstance, (LPSTR)NULL);
  }
  kb2_selected_update();
  if(debug&DEBUGinterface) fprintf(debug_fh, "kb2_selected_create finished\n");
}

static void lampcolor_init()
{
  int i;
  double gamma=1.7;
  double x;
  for(i=0; i<256; i++)
  {
    x=pow(((double)i)/255.0, 1.0/gamma)*255.0;
    lampcolors[i] = RGB(0,(BYTE) (x+0.5),0);
    white_lampcolors[i] = RGB((BYTE) (x+0.5),(BYTE) (x+0.5),(BYTE) (x+0.5));
    if(debug&DEBUGinterface) fprintf(debug_fh, "lampcolor[%d]: %g %8.8x\n", i, x, lampcolors[i]);
  }
  if(debug&DEBUGinterface)
  {
    fflush(debug_fh);
    fprintf(debug_fh, "lampcolor_init finished.\n");
    fflush(debug_fh);
  }
}

kb2_init()
{
  int i;
  double x;
  if(debug&DEBUGinterface) fprintf(debug_fh, "kb2_init called\n");
  lampcolor_init();
  kb2InitApplication(mainhInstance);
  kb2InitInstance(mainhInstance, SW_SHOWNORMAL);
  kb2_selected_create();
  update_viewmenu();
  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "kb2_init finished\n");
    fflush(debug_fh);
  }
}

kb2_destroy()
{
  if(kb2hwnd != NULL)
  {
    DestroyWindow(kb2hwnd);
    kb2hwnd=NULL;
    update_viewmenu();
  }
}
BOOL kb2_is_visible()
{
  return kb2hwnd != NULL;
}

kb2_selected_update()
{
  int i,j;
  if(debug&DEBUGinterface) fprintf(debug_fh, "kb2_selected_update called\n");
  if(kb2hwnd == NULL) return;
  for(i=1; i<5; i++)
  for(j=1; j<5; j++)
  {
    set_check(select_out[i][j], (selected_out[i]&(1<<(j+2)))?True:False);
  }
}

static void nimbi_draw_bit(HDC hdc, int x, int y, int status)
{
  HPEN hpen, hpenold;
  HBRUSH hbr, hbrold;
  hbr = CreateSolidBrush(white_lampcolors[status?255:0]);
  hbrold = SelectObject(hdc, hbr);
  DeleteObject(hbrold);
  hpen = CreatePen(PS_SOLID, 2, white_lampcolors[0]);
  hpenold = SelectObject(hdc, hpen);
  Ellipse(hdc, x-NIMBI_radius, y-NIMBI_radius, x+NIMBI_radius, y+NIMBI_radius);
  SelectObject(hdc, hpenold);
  DeleteObject(hbr);
  DeleteObject(hpen);
}

void nimbi_board_poll()
{
}

void nimbi_update()
{
  HDC hdc;

  if(debug&DEBUGinterface) fprintf(debug_fh, "nimbi_update called\n");
  if(nimbihwnd == NULL) return;
  if(kb12_count==0) kb12_count=1;

  hdc = GetDC(nimbihwnd);
  nimbi_draw_bit(hdc, NIMBI_28x, NIMBI_28y, nimbi_count[0]);
  nimbi_draw_bit(hdc, NIMBI_29x, NIMBI_29y, nimbi_count[1]);
  nimbi_draw_bit(hdc, NIMBI_30x, NIMBI_30y, nimbi_count[2]);
  nimbi_draw_bit(hdc, NIMBI_31x, NIMBI_31y, nimbi_count[3]);
  nimbi_draw_bit(hdc, NIMBI_32x, NIMBI_32y, nimbi_count[4]);
  nimbi_draw_bit(hdc, NIMBI_33x, NIMBI_33y, nimbi_count[5]);
  nimbi_draw_bit(hdc, NIMBI_34x, NIMBI_34y, nimbi_count[6]);
  nimbi_draw_bit(hdc, NIMBI_35x, NIMBI_35y, nimbi_count[7]);
  nimbi_draw_bit(hdc, NIMBI_36x, NIMBI_36y, nimbi_count[8]);
  nimbi_draw_bit(hdc, NIMBI_37x, NIMBI_37y, nimbi_count[9]);
  nimbi_draw_bit(hdc, NIMBI_38x, NIMBI_38y, nimbi_count[10]);
  nimbi_draw_bit(hdc, NIMBI_39x, NIMBI_39y, nimbi_count[11]);
  ReleaseDC(nimbihwnd, hdc);
}

LRESULT CALLBACK nimbiWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;
  HDC hdcBitmap;
  HBITMAP hbmp, hbmpOld;
  BITMAP Bitmap_Info;
  LONG bottom, right;
  int changed=0;

  PAINTSTRUCT ps;

  switch(message)
  {
  case WM_COMMAND:
    if(debug&DEBUGinterface) fprintf(debug_fh, "nimbi WM_COMMAND. notifycode: %d, id: %d, wndCtl: %ld\n", HIWORD(wParam), LOWORD(wParam), lParam);
    break;
  case WM_PAINT:
    hdc = BeginPaint(hWnd, &ps);
    if(debug&DEBUGinterface)
    {
      fprintf(debug_fh, "nimbi WM_PAINT.\n");
      fprintf(debug_fh, "left: %d, right: %d, top: %d, bottom: %d\n",
             ps.rcPaint.left, ps.rcPaint.right, ps.rcPaint.top, ps.rcPaint.bottom);
    }
    hbmp = LoadBitmap(mainhInstance, "nimbi_bitmap");
    GetObject(hbmp, sizeof(Bitmap_Info), &Bitmap_Info);
    hdcBitmap = CreateCompatibleDC(hdc);
    hbmpOld = SelectObject(hdcBitmap, hbmp);
    if(debug&DEBUGinterface) fprintf(debug_fh, "nimbi bitmap. width: %d height: %d\n", Bitmap_Info.bmWidth, Bitmap_Info.bmHeight);
    right = ps.rcPaint.right;
    if(right >= Bitmap_Info.bmWidth) right=Bitmap_Info.bmWidth-1;
    bottom = ps.rcPaint.bottom;
    if(bottom >= Bitmap_Info.bmHeight) bottom=Bitmap_Info.bmHeight-1;
    BitBlt(hdc, ps.rcPaint.left, ps.rcPaint.top, right-ps.rcPaint.left+1, bottom-ps.rcPaint.top+1,
           hdcBitmap, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
    if(debug&DEBUGinterface) fprintf(debug_fh, "bitblt: %d %d %d %d < %d %d\n",
      ps.rcPaint.left, ps.rcPaint.top, right-ps.rcPaint.left+1, bottom-ps.rcPaint.top+1,
      ps.rcPaint.left, ps.rcPaint.top);
    SelectObject(hdcBitmap, hbmpOld);
    DeleteDC(hdcBitmap);
    DeleteObject(hbmp);
    DeleteObject(hbmpOld);
    EndPaint(hWnd, &ps);
    nimbi_update();
    break;
  case WM_LBUTTONDOWN:
    if(debug&DEBUGinterface) fprintf(debug_fh, "x: %d y: %d\n", MAKEPOINTS(lParam).x, MAKEPOINTS(lParam).y);
    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;
    }
    if(changed)
    {
      kb12_reset();
      kb12_update();
      kb1_update();
      kb2_update();
      nimbi_update();
      kb12_reset();
    }
    break;
  case WM_RBUTTONDOWN:
    break;
  case WM_DESTROY:
    nimbihwnd = NULL;
    update_viewmenu();
    break;
  default:
    return(DefWindowProc(hWnd, message, wParam, lParam));
  }
  return 0;
}

static BOOL nimbiInitApplication(HINSTANCE hInstance)
{
  WNDCLASS wc;
  BOOL rc;

  wc.style = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc = nimbiWndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = NULL;
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
  wc.lpszMenuName = NULL;
  wc.lpszClassName = "GIERnimbi";

  rc=RegisterClass(&wc);
  return rc;
}

static BOOL nimbiInitInstance(HINSTANCE hinstance, INT ncmdshow)
{
  RECT warea;

  warea.left = 0;
  warea.right = NIMBI_width-1;
  warea.top = 0;
  warea.bottom = NIMBI_height-1;
  AdjustWindowRect(&warea, WS_OVERLAPPEDWINDOW, False);
  /*  */
  nimbihwnd = CreateWindow("GIERnimbi", "GIER NIMBI Board", WS_OVERLAPPEDWINDOW,
         740, 20, warea.right-warea.left+1, warea.bottom-warea.top+1,
	 (HWND)NULL,(HMENU)NULL, hinstance, (LPSTR)NULL);

  if(nimbihwnd == NULL) return 0;

  ShowWindow(nimbihwnd, ncmdshow);
  UpdateWindow(nimbihwnd);

  return 1;
}

nimbi_init()
{
  int i;
  double x;
  lampcolor_init();
  nimbiInitApplication(mainhInstance);
  nimbiInitInstance(mainhInstance, SW_SHOWNORMAL);
  update_viewmenu();
}

nimbi_destroy()
{
  if(nimbihwnd != NULL)
  {
    DestroyWindow(nimbihwnd);
    nimbihwnd=NULL;
    update_viewmenu();
  }
}
BOOL nimbi_is_visible()
{
  return nimbihwnd != NULL;
}
/*
	typewriter_select_active:

	0: Nothing selected
	1: Selected finished
	2: Selection ongoing

*/
static int typewriter_select_active=0;
static int typewriter_select_row1, typewriter_select_col1, 
	   typewriter_select_row2, typewriter_select_col2,
	   typewriter_select_rowfirst, typewriter_select_colfirst;

static int typewriter_inselection(int row, int col)
{
  row+=scrollpos;
  if(typewriter_select_active==0) return 0;
  if(row<typewriter_select_row1) return 0;
  if(row>typewriter_select_row2) return 0;
  if(row==typewriter_select_row1 && col<typewriter_select_col1) return 0;
  if(row==typewriter_select_row2 && col>typewriter_select_col2) return 0;
  return 1;
}


static void typewriter_drawchar(int row, int col)
{
  HDC hdc;
  HBRUSH hbr, hbrold;
  short c;
  int overprint,selected;
  int x,y;
  char text[3];
  int tlength;
  int oldmode;

  if(debug&DEBUGinterface) fprintf(debug_fh, "typewriter_drawchar: row: %d col: %d scrollpos: %d\n",row,col,scrollpos);

  if((row+scrollpos)<0 || (row+scrollpos)>=LINEBUFFER) return;
  selected=typewriter_inselection(row,col);
  overprint=0;

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

  hdc = GetDC(typewriterhwnd);
  oldfont=SelectObject(hdc, font);
  if(selected)
  {
    SetTextColor(hdc, RGB(255,255,255));
    SetBkColor(hdc, RGB(0,0,0));
  }
  else
  {
    SetTextColor(hdc, RGB(0,0,0));
    SetBkColor(hdc, RGB(255,255,255));
  }
  TextOut(hdc, x, y, " ", 1);

  while( (overprint<MAXOVERWRITE) &&
         ((c=paper[row+scrollpos][col][overprint++]) != 0) )
  {
    if(c&RED_MASK) SetTextColor(hdc, RGB(255,0,0));
    if(c&SYMBOL_MASK) SelectObject(hdc, symbol_font);
    text[0] = (char) (c&255);
    tlength=1;
    if(text[0] == ' ') continue;

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

      y=y+font_height/2;
      SelectObject(hdc, small_font);
    }

    if(overprint > 1)
    {
      oldmode=SetBkMode(hdc, TRANSPARENT);
      hbr = GetStockObject(NULL_BRUSH);
      hbrold = SelectObject(hdc, hbr);
      TextOut(hdc, x, y, text, tlength);
      SelectObject(hdc, hbrold);
      DeleteObject(hbr);
      SetBkMode(hdc, oldmode);
    }
    else
    {
      TextOut(hdc, x, y, text, tlength);
    }
    if(selected)
    {
      if(c&RED_MASK) SetTextColor(hdc, RGB(255,255,255));
    }
    else
    {
      if(c&RED_MASK) SetTextColor(hdc, RGB(0,0,0));
    }
  }
  SelectObject(hdc, oldfont);
  ReleaseDC(typewriterhwnd, hdc);
}

static void typewriter_fakedraw(int row)
{
  HDC hdc;
  HBRUSH hbr, hbrold;
  short c;
  int overprint,selected;
  int x,y,col;
  char text[PAPERWIDTH+1];
  int tlength;
  int oldmode;

  if((row+scrollpos)>=LINEBUFFER) return;

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

  hdc = GetDC(typewriterhwnd);
  oldfont=SelectObject(hdc, font);
    SetTextColor(hdc, RGB(0,0,0));
    SetBkColor(hdc, RGB(255,255,255));
  if((row+scrollpos)%2==1)
  {
    for(col=0;col<PAPERWIDTH;col++) text[col]='0'+(col%10);
  }
  else
  {
    for(col=0;col<PAPERWIDTH;col++) text[col]=' ';
  }
  text[PAPERWIDTH]='\0';
  TextOut(hdc, x, y, text, PAPERWIDTH);
  sprintf(text,"row %4d", row+scrollpos);
  TextOut(hdc, x, y, text, strlen(text));

  SelectObject(hdc, oldfont);
  ReleaseDC(typewriterhwnd, hdc);
}
static void typewriter_update(int row1,int row2,int col1, int col2)
{
  int row,col;

  if(debug&DEBUGinterface) fprintf(debug_fh, "typewriter_update called: %d %d %d %d\n",row1,row2,col1,col2);
  if(typewriterhwnd == NULL) return;

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

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

  /*
  for(row=0; row<LINEBUFFER; row++)
  {
    typewriter_fakedraw(row);
  }
  return;
  */

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

static void savechar(char c)
{
  if( ((c&128)==0) != (typewriter_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 typewriter_copy(char *buffer)
{
  int len;
  int row,col;
  char linebuf[PAPERWIDTH*10];
  int linelen,isred,textstarted,overprint,ired,ifont;
  short c;
  unsigned char text[3];

  isred=0;
  len=0;
  textstarted=0;
  text[1] = '\0';
  if(buffer) buffer[0] = '\0';
  if(debug&DEBUGinterface) fprintf(debug_fh, "typewriter_copy typewriter_select_row1: %d typewriter_select_col1: %d typewriter_select_row2: %d typewriter_select_col2: %d\n", typewriter_select_row1, typewriter_select_col1, typewriter_select_row2, typewriter_select_col2);

  for(row=typewriter_select_row1; row<=typewriter_select_row2; row++)
  {
    /* if(debug&DEBUGinterface) fprintf(debug_fh, "typewriter_copy row: %d started: %d\n", row, textstarted); */
    linelen=0;
    linebuf[0] = '\0';
    for(col=(row==typewriter_select_row1?typewriter_select_col1:0);
	col<=(row==typewriter_select_row2?typewriter_select_col2:(PAPERWIDTH-1));
	col++)
    {
      /* if(debug&DEBUGinterface) fprintf(debug_fh, "typewriter_copy row: %d col: %d\n", row, col); */
      overprint=0;
      while( (overprint<MAXOVERWRITE) &&
	  (c=paper[row][col][overprint++]) != 0)
      {
        /* if(debug&DEBUGinterface)
	{
	  fprintf(debug_fh, "typewriter_copy row: %d col: %d overprint: %d c: %d\n", row, col, overprint, c);
	  fflush(debug_fh);
	} */
	ired = (c&RED_MASK)!=0;
	ifont = (c&SYMBOL_MASK)!=0;
	text[0] = (unsigned char) (c&255);
	if(ired != isred)
	{
	  if(ired)
	    strcat(linebuf, "#029");
	  else
	    strcat(linebuf, "#062");
	  isred=ired;
	}
	if(text[0] != ' ')
	{
	  textstarted=1;
	}
	if(text[0] != ' ' || textstarted)
	{
	  if(ifont)
	  {
	    if(text[0] == 218) strcat(linebuf, "£");
	    else if(text[0] == 180) strcat(linebuf, "*");
	    else if(text[0] == 217) strcat(linebuf, "&");
	  }
	  else
	    strcat(linebuf, text);
	}
      }
    }
    linelen=strlen(linebuf);
    while(linelen>0 && linebuf[linelen-1]==' ')
    {
      linebuf[linelen-1]='\0';
      linelen--;
    }
    linebuf[linelen++] = '\r';
    linebuf[linelen++] = '\n';
    linebuf[linelen] = '\0';
    if(textstarted)
    {
      if(buffer) strcat(buffer, linebuf);
      len+=linelen;
    }
  }
  if(isred)
  {
    if(buffer)
    {
      /* remove crlf */
      buffer[strlen(buffer)-2]='\0';
      strcat(buffer, "#062\r\n");
    }
    len+=4;
  }
  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "typewriter_copy len: %d\n", len);
    if(buffer)
      fprintf(debug_fh, "                strlen(buffer): %d\n", strlen(buffer));
    else
      fprintf(debug_fh, "                buffer: NULL\n");
  }
  return(len);
}

static void typewriter_copy_clipboard()
{
  int cliplen,i;
  char *clip;
  LPTSTR  lptstrCopy; 
  HGLOBAL hglbCopy; 

  cliplen=typewriter_copy(NULL);
  clip=malloc(cliplen+1);
  typewriter_copy(clip);
  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "clip:\n====\n%s====\n",clip);
    for(i=0;i<cliplen;i++)
    {
      fprintf(debug_fh, "i: %d clip[i]: %d\n", i, clip[i]);
    }
  }

  if (!OpenClipboard(mainhwnd)) 
  {
    free(clip);
    return; 
  }
  EmptyClipboard(); 
  hglbCopy = GlobalAlloc(GMEM_MOVEABLE, (cliplen + 1) * sizeof(TCHAR)); 
  if (hglbCopy == NULL) 
  { 
    CloseClipboard(); 
    free(clip);
    return; 
  } 

  lptstrCopy = GlobalLock(hglbCopy); 
  memcpy(lptstrCopy, clip, cliplen);
  lptstrCopy[cliplen] = (TCHAR) 0;    // null character 
  GlobalUnlock(hglbCopy); 

  SetClipboardData(CF_TEXT, hglbCopy); 

  CloseClipboard(); 
  free(clip);

}

static void typewriter_paste_clipboard()
{
  LPTSTR    lptstr; 
  HGLOBAL   hglb;

  if(debug&DEBUGinterface) fprintf(debug_fh, "typewriter_paste_clipboard: kilroy 1.\n");
  if (!IsClipboardFormatAvailable(CF_TEXT)) return; 
  if(debug&DEBUGinterface) fprintf(debug_fh, "typewriter_paste_clipboard: kilroy 2.\n");
  if (!OpenClipboard(mainhwnd)) return; 
  if(debug&DEBUGinterface) fprintf(debug_fh, "typewriter_paste_clipboard: kilroy 3.\n");
 
  hglb = GetClipboardData(CF_TEXT); 
  if (hglb != NULL) 
  { 
    if(debug&DEBUGinterface) fprintf(debug_fh, "typewriter_paste_clipboard: kilroy 4.\n");
    lptstr = GlobalLock(hglb); 
    if (lptstr != NULL) 
    { 
      if(debug&DEBUGinterface) fprintf(debug_fh, "typewriter_paste_clipboard: kilroy 5: \"%s\"\n", lptstr);
      typewriter_clipboard_input_len=strlen(lptstr);
      typewriter_clipboard_input=malloc(typewriter_clipboard_input_len+1);
      strcpy(typewriter_clipboard_input, lptstr);
      typewriter_clipboard_input_pnt=0;

      GlobalUnlock(hglb); 
      if(typewriter_waiting) typewriter_check_paste();
    } 
    CloseClipboard(); 
  }
}

static void typewriter_check_paste()
{
  unsigned char ch2;
  if(debug&DEBUGinterface) fprintf(debug_fh, "typewriter_check_paste called I.\n");
  if(typewriter_clipboard_input==NULL) return;
  if(debug&DEBUGinterface) fprintf(debug_fh, "typewriter_check_paste called II.\n");
  if(typewriter_clipboard_input[typewriter_clipboard_input_pnt]==0)
  {
clipexhausted:
    if(debug&DEBUGinterface) fprintf(debug_fh, "typewriter_check_paste clipboard exhausted.\n");
    free(typewriter_clipboard_input);
    typewriter_clipboard_input=NULL;
    return;
  }
  typewriter_old_uppercase = typewriter_uppercase;

  while(typewriter_clipboard_input[typewriter_clipboard_input_pnt]=='\r') typewriter_clipboard_input_pnt++;
  if(!typewriter_clipboard_input[typewriter_clipboard_input_pnt]) goto clipexhausted;

  ch2 = a2flx(typewriter_clipboard_input[typewriter_clipboard_input_pnt++], &typewriter_uppercase);

  if(ch2 != 255)
  {
    if(debug&DEBUGinterface) fprintf(debug_fh, "typewriter_check_paste add char %d.\n",ch2);
    savechar(ch2+128*typewriter_uppercase);
    YE_wait=0;
    typewriter_waiting=0;
  }
}

static void keystrokes(int k)
{
  switch(k)
  {
    case VK_LBUTTON: fprintf(debug_fh, "0x01: Left mouse button"); break;
    case VK_RBUTTON: fprintf(debug_fh, "0x02: Right mouse button"); break;
    case VK_CANCEL: fprintf(debug_fh, "0x03: Control-break processing"); break;
    case VK_MBUTTON: fprintf(debug_fh, "0x04: Middle mouse button (three-button mouse)"); break;
    case VK_XBUTTON1: fprintf(debug_fh, "0x05: X1 mouse button"); break;
    case VK_XBUTTON2: fprintf(debug_fh, "0x06: X2 mouse button"); break;
    case VK_BACK: fprintf(debug_fh, "0x08: BACKSPACE key"); break;
    case VK_TAB: fprintf(debug_fh, "0x09: TAB key"); break;
    case VK_CLEAR: fprintf(debug_fh, "0x0C: CLEAR key"); break;
    case VK_RETURN: fprintf(debug_fh, "0x0D: ENTER key"); break;
    case VK_SHIFT: fprintf(debug_fh, "0x10: SHIFT key"); break;
    case VK_CONTROL: fprintf(debug_fh, "0x11: CTRL key"); break;
    case VK_MENU: fprintf(debug_fh, "0x12: ALT key"); break;
    case VK_PAUSE: fprintf(debug_fh, "0x13: PAUSE key"); break;
    case VK_CAPITAL: fprintf(debug_fh, "0x14: CAPS LOCK key"); break;
    case VK_KANA: fprintf(debug_fh, "0x15: IME Kana mode"); break;
    case VK_JUNJA: fprintf(debug_fh, "0x17: IME Junja mode"); break;
    case VK_FINAL: fprintf(debug_fh, "0x18: IME final mode"); break;
    case VK_KANJI: fprintf(debug_fh, "0x19: IME Kanji mode"); break;
    case VK_ESCAPE: fprintf(debug_fh, "0x1B: ESC key"); break;
    case VK_CONVERT: fprintf(debug_fh, "0x1C: IME convert"); break;
    case VK_NONCONVERT: fprintf(debug_fh, "0x1D: IME nonconvert"); break;
    case VK_ACCEPT: fprintf(debug_fh, "0x1E: IME accept"); break;
    case VK_MODECHANGE: fprintf(debug_fh, "0x1F: IME mode change request"); break;
    case VK_SPACE: fprintf(debug_fh, "0x20: SPACEBAR"); break;
    case VK_PRIOR: fprintf(debug_fh, "0x21: PAGE UP key"); break;
    case VK_NEXT: fprintf(debug_fh, "0x22: PAGE DOWN key"); break;
    case VK_END: fprintf(debug_fh, "0x23: END key"); break;
    case VK_HOME: fprintf(debug_fh, "0x24: HOME key"); break;
    case VK_LEFT: fprintf(debug_fh, "0x25: LEFT ARROW key"); break;
    case VK_UP: fprintf(debug_fh, "0x26: UP ARROW key"); break;
    case VK_RIGHT: fprintf(debug_fh, "0x27: RIGHT ARROW key"); break;
    case VK_DOWN: fprintf(debug_fh, "0x28: DOWN ARROW key"); break;
    case VK_SELECT: fprintf(debug_fh, "0x29: SELECT key"); break;
    case VK_PRINT: fprintf(debug_fh, "0x2A: PRINT key"); break;
    case VK_EXECUTE: fprintf(debug_fh, "0x2B: EXECUTE key"); break;
    case VK_SNAPSHOT: fprintf(debug_fh, "0x2C: PRINT SCREEN key"); break;
    case VK_INSERT: fprintf(debug_fh, "0x2D: INS key"); break;
    case VK_DELETE: fprintf(debug_fh, "0x2E: DEL key"); break;
    case VK_HELP: fprintf(debug_fh, "0x2F: HELP key"); break;
    case VK_LWIN: fprintf(debug_fh, "0x5B: Left Windows key (Natural keyboard)"); break;
    case VK_RWIN: fprintf(debug_fh, "0x5C: Right Windows key (Natural keyboard)"); break;
    case VK_APPS: fprintf(debug_fh, "0x5D: Applications key (Natural keyboard)"); break;
    case VK_SLEEP: fprintf(debug_fh, "0x5F: Computer Sleep key"); break;
    case VK_NUMPAD0: fprintf(debug_fh, "0x60: Numeric keypad 0 key"); break;
    case VK_NUMPAD1: fprintf(debug_fh, "0x61: Numeric keypad 1 key"); break;
    case VK_NUMPAD2: fprintf(debug_fh, "0x62: Numeric keypad 2 key"); break;
    case VK_NUMPAD3: fprintf(debug_fh, "0x63: Numeric keypad 3 key"); break;
    case VK_NUMPAD4: fprintf(debug_fh, "0x64: Numeric keypad 4 key"); break;
    case VK_NUMPAD5: fprintf(debug_fh, "0x65: Numeric keypad 5 key"); break;
    case VK_NUMPAD6: fprintf(debug_fh, "0x66: Numeric keypad 6 key"); break;
    case VK_NUMPAD7: fprintf(debug_fh, "0x67: Numeric keypad 7 key"); break;
    case VK_NUMPAD8: fprintf(debug_fh, "0x68: Numeric keypad 8 key"); break;
    case VK_NUMPAD9: fprintf(debug_fh, "0x69: Numeric keypad 9 key"); break;
    case VK_MULTIPLY: fprintf(debug_fh, "0x6A: Multiply key"); break;
    case VK_ADD: fprintf(debug_fh, "0x6B: Add key"); break;
    case VK_SEPARATOR: fprintf(debug_fh, "0x6C: Separator key"); break;
    case VK_SUBTRACT: fprintf(debug_fh, "0x6D: Subtract key"); break;
    case VK_DECIMAL: fprintf(debug_fh, "0x6E: Decimal key"); break;
    case VK_DIVIDE: fprintf(debug_fh, "0x6F: Divide key"); break;
    case VK_F1: fprintf(debug_fh, "0x70: F1 key"); break;
    case VK_F2: fprintf(debug_fh, "0x71: F2 key"); break;
    case VK_F3: fprintf(debug_fh, "0x72: F3 key"); break;
    case VK_F4: fprintf(debug_fh, "0x73: F4 key"); break;
    case VK_F5: fprintf(debug_fh, "0x74: F5 key"); break;
    case VK_F6: fprintf(debug_fh, "0x75: F6 key"); break;
    case VK_F7: fprintf(debug_fh, "0x76: F7 key"); break;
    case VK_F8: fprintf(debug_fh, "0x77: F8 key"); break;
    case VK_F9: fprintf(debug_fh, "0x78: F9 key"); break;
    case VK_F10: fprintf(debug_fh, "0x79: F10 key"); break;
    case VK_F11: fprintf(debug_fh, "0x7A: F11 key"); break;
    case VK_F12: fprintf(debug_fh, "0x7B: F12 key"); break;
    case VK_F13: fprintf(debug_fh, "0x7C: F13 key"); break;
    case VK_F14: fprintf(debug_fh, "0x7D: F14 key"); break;
    case VK_F15: fprintf(debug_fh, "0x7E: F15 key"); break;
    case VK_F16: fprintf(debug_fh, "0x7F: F16 key"); break;
    case VK_F17: fprintf(debug_fh, "0x80: F17 key"); break;
    case VK_F18: fprintf(debug_fh, "0x81: F18 key"); break;
    case VK_F19: fprintf(debug_fh, "0x82: F19 key"); break;
    case VK_F20: fprintf(debug_fh, "0x83: F20 key"); break;
    case VK_F21: fprintf(debug_fh, "0x84: F21 key"); break;
    case VK_F22: fprintf(debug_fh, "0x85: F22 key"); break;
    case VK_F23: fprintf(debug_fh, "0x86: F23 key"); break;
    case VK_F24: fprintf(debug_fh, "0x87: F24 key"); break;
    case VK_NUMLOCK: fprintf(debug_fh, "0x90: NUM LOCK key"); break;
    case VK_SCROLL: fprintf(debug_fh, "0x91: SCROLL LOCK key"); break;
    case VK_LSHIFT: fprintf(debug_fh, "0xA0: Left SHIFT key"); break;
    case VK_RSHIFT: fprintf(debug_fh, "0xA1: Right SHIFT key"); break;
    case VK_LCONTROL: fprintf(debug_fh, "0xA2: Left CONTROL key"); break;
    case VK_RCONTROL: fprintf(debug_fh, "0xA3: Right CONTROL key"); break;
    case VK_LMENU: fprintf(debug_fh, "0xA4: Left MENU key"); break;
    case VK_RMENU: fprintf(debug_fh, "0xA5: Right MENU key"); break;
    case VK_BROWSER_BACK: fprintf(debug_fh, "0xA6: Browser Back key"); break;
    case VK_BROWSER_FORWARD: fprintf(debug_fh, "0xA7: Browser Forward key"); break;
    case VK_BROWSER_REFRESH: fprintf(debug_fh, "0xA8: Browser Refresh key"); break;
    case VK_BROWSER_STOP: fprintf(debug_fh, "0xA9: Browser Stop key"); break;
    case VK_BROWSER_SEARCH: fprintf(debug_fh, "0xAA: Browser Search key"); break;
    case VK_BROWSER_FAVORITES: fprintf(debug_fh, "0xAB: Browser Favorites key"); break;
    case VK_BROWSER_HOME: fprintf(debug_fh, "0xAC: Browser Start and Home key"); break;
    case VK_VOLUME_MUTE: fprintf(debug_fh, "0xAD: Volume Mute key"); break;
    case VK_VOLUME_DOWN: fprintf(debug_fh, "0xAE: Volume Down key"); break;
    case VK_VOLUME_UP: fprintf(debug_fh, "0xAF: Volume Up key"); break;
    case VK_MEDIA_NEXT_TRACK: fprintf(debug_fh, "0xB0: Next Track key"); break;
    case VK_MEDIA_PREV_TRACK: fprintf(debug_fh, "0xB1: Previous Track key"); break;
    case VK_MEDIA_STOP: fprintf(debug_fh, "0xB2: Stop Media key"); break;
    case VK_MEDIA_PLAY_PAUSE: fprintf(debug_fh, "0xB3: Play/Pause Media key"); break;
    case VK_LAUNCH_MAIL: fprintf(debug_fh, "0xB4: Start Mail key"); break;
    case VK_LAUNCH_MEDIA_SELECT: fprintf(debug_fh, "0xB5: Select Media key"); break;
    case VK_LAUNCH_APP1: fprintf(debug_fh, "0xB6: Start Application 1 key"); break;
    case VK_LAUNCH_APP2: fprintf(debug_fh, "0xB7: Start Application 2 key"); break;
    case VK_OEM_1: fprintf(debug_fh, "0xBA: Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ';:' key"); break;
    case VK_OEM_PLUS: fprintf(debug_fh, "0xBB: For any country/region, the '+' key"); break;
    case VK_OEM_COMMA: fprintf(debug_fh, "0xBC: For any country/region, the ',' key"); break;
    case VK_OEM_MINUS: fprintf(debug_fh, "0xBD: For any country/region, the '-' key"); break;
    case VK_OEM_PERIOD: fprintf(debug_fh, "0xBE: For any country/region, the '.' key"); break;
    case VK_OEM_2: fprintf(debug_fh, "0xBF: Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '/?' key"); break;
    case VK_OEM_3: fprintf(debug_fh, "0xC0: Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '`~' key"); break;
    case VK_OEM_4: fprintf(debug_fh, "0xDB: Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '[{' key"); break;
    case VK_OEM_5: fprintf(debug_fh, "0xDC: Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '\\|' key"); break;
    case VK_OEM_6: fprintf(debug_fh, "0xDD: Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ']}' key"); break;
    case VK_OEM_7: fprintf(debug_fh, "0xDE: Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the 'single-quote/double-quote' key"); break;
    case VK_OEM_8: fprintf(debug_fh, "0xDF: Used for miscellaneous characters; it can vary by keyboard."); break;
    case VK_OEM_102: fprintf(debug_fh, "0xE2: Either the angle bracket key or the backslash key on the RT 102-key keyboard"); break;
    case VK_PROCESSKEY: fprintf(debug_fh, "0xE5: IME PROCESS key"); break;
    case VK_PACKET: fprintf(debug_fh, "0xE7: Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods. For more information, see Remark in KEYBDINPUT, SendInput, WM_KEYDOWN, and WM_KEYUP"); break;
    case VK_ATTN: fprintf(debug_fh, "0xF6: Attn key"); break;
    case VK_CRSEL: fprintf(debug_fh, "0xF7: CrSel key"); break;
    case VK_EXSEL: fprintf(debug_fh, "0xF8: ExSel key"); break;
    case VK_EREOF: fprintf(debug_fh, "0xF9: Erase EOF key"); break;
    case VK_PLAY: fprintf(debug_fh, "0xFA: Play key"); break;
    case VK_ZOOM: fprintf(debug_fh, "0xFB: Zoom key"); break;
    case VK_NONAME: fprintf(debug_fh, "0xFC: Reserved"); break;
    case VK_PA1: fprintf(debug_fh, "0xFD: PA1 key"); break;
    case VK_OEM_CLEAR: fprintf(debug_fh, "0xFE: Clear key"); break;
    default: fprintf(debug_fh, "Code: %02x",k); break;
  }
  fprintf(debug_fh, "\n");
}

static int uclc=0;
static int shift_key=0;
static int ctrl_key=0;

static void set_sbar(int pos)
{
  SCROLLINFO si;
  RECT rect;
  GetClientRect(typewriterhwnd, &rect);
  typewriter_nlines=(rect.bottom-rect.top)/font_height-1;

  if(debug&DEBUGinterface) fprintf(debug_fh, "set_sbar: %d typewriter_nlines: %d\n", pos, typewriter_nlines);
  if(pos<0 || pos>(LINEBUFFER-typewriter_nlines)) pos=LINEBUFFER-typewriter_nlines;
  si.cbSize = sizeof(si);
  // si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
  si.fMask = SIF_ALL;
  si.nMin = 0;
  si.nMax = LINEBUFFER - 1;
  si.nPage = typewriter_nlines;
  si.nPos = pos;
  scrollpos=pos;
  SetScrollInfo(typewriterhwnd, SB_VERT, &si, TRUE);
}

LRESULT CALLBACK typewriterWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;
  int i;
  int status;
  int x,y,row1,row2,col1,col2;
  int update,minrow,maxrow;
  char ch1;
  unsigned char ch2;

  PAINTSTRUCT ps;
  PrintMessage(message);
  switch(message)
  {
  case WM_COMMAND:
    if(debug&DEBUGinterface) fprintf(debug_fh, "typewriterWndProc. WM_COMMAND.\n");
    break;
  case WM_PAINT:
    hdc = BeginPaint(hWnd, &ps);
    row1=ps.rcPaint.top/font_height-1;
    row2=(ps.rcPaint.bottom+font_height-1)/font_height-1;
    col1=ps.rcPaint.left/font_width;
    col2=(ps.rcPaint.right+font_width-1)/font_width;
    if(debug&DEBUGinterface)
    {
      fprintf(debug_fh, "typewriter WM_PAINT.\n");
      fprintf(debug_fh, "left: %d, right: %d, top: %d, bottom: %d\n",
             ps.rcPaint.left, ps.rcPaint.right, ps.rcPaint.top, ps.rcPaint.bottom);
      fprintf(debug_fh, "row1: %d row2: %d col1: %d col2: %d\n", row1, row2, col1, col2);
    }
    EndPaint(hWnd, &ps);
    typewriter_update(row1,row2,col1,col2);
    update_typewriter_waiting();
    break;
  case WM_SIZE:
    set_sbar(-1);
    break;
  case WM_MOUSEWHEEL:
    if(debug&DEBUGinterface) fprintf(debug_fh, "WM_MOUSEWHEEL: %d\n", GET_WHEEL_DELTA_WPARAM(wParam));
    set_sbar(scrollpos-GET_WHEEL_DELTA_WPARAM(wParam)/WHEEL_DELTA);
    typewriter_update(0,LINEBUFFER-1,0,PAPERWIDTH-1);
    break;
  case WM_KEYDOWN:
    if(debug&DEBUGinterface)
    {
      fprintf(debug_fh, "WM_KEYDOWN: keycode: %d ", wParam);
      keystrokes(wParam);
      fprintf(debug_fh, "WM_KEYDOWN: lparam: %8.8x\n", lParam);
    }
    if(wParam == VK_SHIFT)
    {
      shift_key=1;
      uclc=1;
#ifdef TYPEWRITER_SOUND
      if(sound_enabled)
      {
	play_typewriter_sound(typewriter_sound_uc, typewriter_sound_uc_len);
	waveOutReset(hwo);
      }
#endif
    }
    else if(wParam == VK_CONTROL)
    {
      ctrl_key=1;
    }
    break;
  case WM_KEYUP:
    if(debug&DEBUGinterface)
    {
      fprintf(debug_fh, "WM_KEYUP: keycode: %d ", wParam);
      keystrokes(wParam);
      fprintf(debug_fh, "WM_KEYUP: lparam: %8.8x\n", lParam);
    }
    if(wParam == VK_SHIFT)
    {
      shift_key=0;
      if(typewriter_waiting)
      {
#ifdef TYPEWRITER_SOUND
	if(sound_enabled)
	{
	  play_typewriter_sound(typewriter_sound_lc, typewriter_sound_lc_len);
	  waveOutReset(hwo);
	}
#endif
	if(typewriter_nchars<(TYPEWRITER_TABLE_SIZE-2))
	{
	  if(uclc)
	  {
	    typewriter_char_table[typewriter_nchars++] = 60;
	    typewriter_char_table[typewriter_nchars++] = 58;
	    YE_wait=0;
	    uclc=0;
	  }
        }
      }
    }
    else if(wParam == VK_CONTROL)
    {
      ctrl_key=0;
    }
    break;
  case WM_LBUTTONDOWN:
  case WM_LBUTTONUP:
  case WM_MOUSEMOVE:
    x = GET_X_LPARAM(lParam); 
    y = GET_Y_LPARAM(lParam); 
    row1 = y/font_height-1+scrollpos;
    col1 = x/font_width;
    if(debug&DEBUGinterface)
    {
      if(message == WM_LBUTTONDOWN) fprintf(debug_fh, "WM_LBUTTONDOWN");
      else if(message == WM_LBUTTONUP) fprintf(debug_fh, "WM_LBUTTONUP");
      else if(message == WM_MOUSEMOVE) fprintf(debug_fh, "WM_MOUSEMOVE");
      else fprintf(debug_fh, "???");
      fprintf(debug_fh, ": %d %d %d %d wParam: %8.8x\n",x,y,row1,col1,wParam);
    }
    update=0;
    minrow = typewriter_select_rowfirst;
    maxrow = typewriter_select_rowfirst;
    if(message == WM_LBUTTONDOWN)
    {
      if(typewriter_select_active>0) update=2;
      typewriter_select_active=2;
      typewriter_select_rowfirst=row1;
      typewriter_select_colfirst=col1;
      typewriter_select_row1=row1;
      typewriter_select_row2=row1;
      typewriter_select_col1=col1;
      typewriter_select_col2=col1;
    }
    else if(message == WM_LBUTTONUP || (message == WM_MOUSEMOVE && (wParam&MK_LBUTTON)))
    {
      update=1;
      if(typewriter_select_row1 < minrow) minrow=typewriter_select_row1;
      if(typewriter_select_row1 > maxrow) maxrow=typewriter_select_row1;
      if(typewriter_select_row2 < minrow) minrow=typewriter_select_row2;
      if(typewriter_select_row2 > maxrow) maxrow=typewriter_select_row2;
      if(row1 < minrow) minrow=row1;
      if(row1 > maxrow) maxrow=row1;
      if(row1 < typewriter_select_rowfirst || (row1==typewriter_select_rowfirst && col1<typewriter_select_colfirst))
      {
	typewriter_select_row1=row1;
	typewriter_select_col1=col1;
	typewriter_select_row2=typewriter_select_rowfirst;
	typewriter_select_col2=typewriter_select_colfirst;
      }
      else
      {
	typewriter_select_row1=typewriter_select_rowfirst;
	typewriter_select_col1=typewriter_select_colfirst;
	typewriter_select_row2=row1;
	typewriter_select_col2=col1;
      }
      if(message == WM_LBUTTONUP && row1==typewriter_select_rowfirst && col1==typewriter_select_colfirst)
      {
	typewriter_select_active=0;
      }
      else
	typewriter_select_active=(message == WM_MOUSEMOVE)?2:1;
    }
    if(debug&DEBUGinterface) fprintf(debug_fh, "typewriter_select: update: %d minrow: %d maxrow: %d\n", update, minrow, maxrow);
    if(update==2)
      typewriter_update(0,LINEBUFFER-1,0,PAPERWIDTH-1);
    else if(update==1)
      typewriter_update(minrow-scrollpos,maxrow-scrollpos,0,PAPERWIDTH-1);
    break;
  case WM_CHAR:
    if(debug&DEBUGinterface)
    {
      fprintf(debug_fh, "WM_CHAR: keycode: %d shift: %d ctrl: %d\n", wParam,shift_key,ctrl_key);
      fprintf(debug_fh, "WM_CHAR: lparam: %8.8x\n", lParam);
    }
    if(ctrl_key)
    {
      if(shift_key)
      {
	if(wParam==1)
	{
	  /* ctrl-shift-a: select none */
	  typewriter_select_active=0;
	  typewriter_update(0,LINEBUFFER-1,0,PAPERWIDTH-1);
	}
      }
      else
      {
	if(wParam==1)
	{
	  /* ctrl-a: select all */
	  typewriter_select_active=1;
	  typewriter_select_row1=0;
	  typewriter_select_col1=0;
          typewriter_select_row2=LINEBUFFER-1;
          typewriter_select_col2=PAPERWIDTH-1;
	  typewriter_update(0,LINEBUFFER-1,0,PAPERWIDTH-1);
	}
	else if(wParam==3)
	{
	  /* ctrl-c: copy */
	  if(typewriter_select_active>0)
	    typewriter_copy_clipboard();
	}
	else if(wParam==22)
	{
	  /* ctrl-v: paste */
	  typewriter_paste_clipboard();
	}
      }
    }
    else if(typewriter_waiting)
    {
      if(typewriter_nchars<(TYPEWRITER_TABLE_SIZE-1))
      {
        ch1 = (TCHAR) wParam;
	  
	if(debug&DEBUGinterface) fprintf(debug_fh, "  character: %c\n", ch1);

	typewriter_old_uppercase = typewriter_uppercase;
	  
	ch2 = a2flx(ch1, &typewriter_uppercase);

	if(ch2 == 255)
	{
	  if(debug&DEBUGinterface) fprintf(debug_fh, "  Unknown character: %c\n", ch1);
	}
	else
	{
	  savechar(ch2+128*typewriter_uppercase);
	  YE_wait=0;
	  uclc=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");
  }
  break;
      case WM_VSCROLL:
    if(debug&DEBUGinterface) fprintf(debug_fh, "WM_VSCROLL %d\n",LOWORD(wParam));
	switch (LOWORD(wParam)) {
	  case SB_BOTTOM:
	    if(debug&DEBUGinterface) fprintf(debug_fh, "term_scroll(term, -1, 0);\n");
	    break;
	  case SB_TOP:
	    if(debug&DEBUGinterface) fprintf(debug_fh, "term_scroll(term, +1, 0);\n");
	    break;
	  case SB_LINEDOWN:
	    if(debug&DEBUGinterface) fprintf(debug_fh, "term_scroll(term, 0, +1);\n");
	    break;
	  case SB_LINEUP:
	    if(debug&DEBUGinterface) fprintf(debug_fh, "term_scroll(term, 0, -1);\n");
	    break;
	  case SB_PAGEDOWN:
	    if(debug&DEBUGinterface) fprintf(debug_fh, "term_scroll(term, 0, +term->rows / 2);\n");
	    break;
	  case SB_PAGEUP:
	    if(debug&DEBUGinterface) fprintf(debug_fh, "term_scroll(term, 0, -term->rows / 2);\n");
	    break;
	  case SB_THUMBPOSITION:
	  case SB_THUMBTRACK:
	    if(debug&DEBUGinterface) fprintf(debug_fh, "SB_THUMBPOSITION/SB_THUMBTRACK\n");
	    {
		SCROLLINFO si;

		si.cbSize = sizeof(si);
		si.fMask = SIF_TRACKPOS;
		if (GetScrollInfo(typewriterhwnd, SB_VERT, &si) == 0)
		    si.nTrackPos = HIWORD(wParam);
	    if(debug&DEBUGinterface) fprintf(debug_fh, "si.nTrackPos: %d\n", si.nTrackPos);
		// term_scroll(term, 1, si.nTrackPos);
                set_sbar(si.nTrackPos);
      typewriter_update(0,LINEBUFFER-1,0,PAPERWIDTH-1);
	    }
	    break;
	}
	break;
case WM_DESTROY:
  typewriterhwnd = NULL;
  update_viewmenu();
  break;
default:
  return(DefWindowProc(hWnd, message, wParam, lParam));
}
return 0;
}

static BOOL typewriterInitApplication(HINSTANCE hInstance)
{
WNDCLASS wc;
BOOL rc;

wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = typewriterWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = "GIERtypewriter";

rc=RegisterClass(&wc);
return rc;
}

static BOOL typewriterInitInstance(HINSTANCE hinstance, INT ncmdshow)
{
RECT warea,rect;
int sbWidth=10;

warea.left = 0;
warea.right = font_width*80-1+16;
warea.top = 0;
warea.bottom = font_height*(typewriter_nlines+1)-1;
if(debug&DEBUGinterface) fprintf(debug_fh, "typewriterInitInstance I: left: %d right: %d top: %d bottom: %d font_width: %d font_height: %d\n", warea.left,warea.right,warea.top ,warea.bottom, font_width, font_height);
AdjustWindowRect(&warea, WS_OVERLAPPEDWINDOW, False);
if(debug&DEBUGinterface) fprintf(debug_fh, "typewriterInitInstance II: left: %d right: %d top: %d bottom: %d font_width: %d font_height: %d\n", warea.left,warea.right,warea.top ,warea.bottom,font_width, font_height);

typewriterhwnd = CreateWindowEx(0, "GIERtypewriter", "GIER Typewriter", 
       WS_OVERLAPPEDWINDOW|WS_VSCROLL,
       25, 310, warea.right-warea.left+1, warea.bottom-warea.top+1,
       (HWND)NULL,(HMENU)NULL, hinstance, (LPSTR)NULL);

if(typewriterhwnd == NULL) return 0;

GetWindowRect(typewriterhwnd, &rect);
if(debug&DEBUGinterface) fprintf(debug_fh, "typewriterInitInstance III: left: %d right: %d top: %d bottom: %d font_width: %d font_height: %d\n", rect.left,rect.right,rect.top ,rect.bottom,font_width, font_height);

GetClientRect(typewriterhwnd, &rect);
if(debug&DEBUGinterface) fprintf(debug_fh, "typewriterInitInstance IV: left: %d right: %d top: %d bottom: %d font_width: %d font_height: %d\n", rect.left,rect.right,rect.top ,rect.bottom,font_width, font_height);

    set_sbar(-1);

ShowWindow(typewriterhwnd, ncmdshow);
UpdateWindow(typewriterhwnd);


return 1;
}

typewriter_init()
{
int i,j,k;
#ifdef TYPEWRITER_SOUND
int ndev;
MMRESULT status;
WAVEFORMATEX wfx;
FILE *fh;
#endif
typewriterInitApplication(mainhInstance);
typewriterInitInstance(mainhInstance, SW_SHOWNORMAL);
update_viewmenu();

#ifdef TYPEWRITER_SOUND
if(debug&DEBUGinterface) fprintf(debug_fh, "Initialize typewriter sound.\n");
nbuffers = NBUFFERS;
buffersize = BUFFERSIZE;

ndev=waveOutGetNumDevs();
if(debug&DEBUGinterface) fprintf(debug_fh, "No. of devices: %d\n", ndev);
if(ndev==0) return 0;

wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = 1;
wfx.nSamplesPerSec = 44100;
wfx.nAvgBytesPerSec = 88200;
wfx.wBitsPerSample = 16;
wfx.nBlockAlign = (wfx.wBitsPerSample*wfx.nChannels)/8;
wfx.cbSize = sizeof(WAVEFORMATEX);

/* sound_debug("init",3); */
status=waveOutOpen(&hwo, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL|WAVE_ALLOWSYNC);
if(debug&DEBUGinterface) fprintf(debug_fh, "waveOutOpen result: %d, OK is: %d\n", status, MMSYSERR_NOERROR);
if(status != MMSYSERR_NOERROR) return 0;

/*
      Allocate buffers
*/
sbuffers = (char **) malloc(sizeof(*sbuffers)*nbuffers);
whdr = (WAVEHDR *) malloc(sizeof(*whdr)*nbuffers);
/* sound_debug("init",4); */
for(i=0; i<nbuffers; i++)
{
  sbuffers[i] = (char *) malloc(buffersize);
  memset(&whdr[i], 0, sizeof(whdr[i]));
  whdr[i].lpData = sbuffers[i];
  whdr[i].dwBufferLength = buffersize;
  whdr[i].dwFlags = 0;
  whdr[i].dwLoops = 0;
  waveOutPrepareHeader(hwo, &whdr[i], sizeof(whdr[i]));
  if(debug&DEBUGinterface) fprintf(debug_fh, "WHDR_DONE[%d]: %s buffer: %d\n", i,whdr[i].dwFlags&WHDR_DONE?"true":"false", (long) whdr[i].lpData);
  whdr[i].dwFlags |= WHDR_DONE;
}
ibuffer = 0;
ipnt = 0;

#endif
}

typewriter_destroy()
{
if(typewriterhwnd != NULL)
{
  DestroyWindow(typewriterhwnd);
  typewriterhwnd=NULL;
  update_viewmenu();
}
}
typewriter_lowercase()
{
/* Called by Reset/Mikrotempi stop */
typewriter_uppercase=0;
typewriter_nchars=0;
}

BOOL typewriter_is_visible()
{
return typewriterhwnd != NULL;
}

static void update_typewriter_waiting()
{
HDC hdc;
HFONT oldfont;
if(debug&DEBUGinterface)
{
  fprintf(debug_fh, "update_typewriter_waiting called: %d\n",typewriter_waiting);
  fflush(debug_fh);
}
hdc = GetDC(typewriterhwnd);
oldfont=SelectObject(hdc, font);
SetTextColor(hdc, RGB(0,255,0));
SetBkColor(hdc, RGB(255,255,255));
TextOut(hdc, typewriter_CursorX(70), typewriter_CursorY(-1), typewriter_waiting?"INPUT":"     ", 5);
SelectObject(hdc, oldfont);
ReleaseDC(typewriterhwnd, hdc);
if(debug&DEBUGinterface)
{
  fprintf(debug_fh, "update_typewriter_waiting finished\n");
  fflush(debug_fh);
}
}

void typewriter_wait(int status)
{
int old_status;
HDC hdc;

old_status = typewriter_waiting;

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

if(old_status != typewriter_waiting && typewriterhwnd != NULL)
{
  update_typewriter_waiting();
}
}

static void typewriter_scroll()
{
int i,j,k;
RECT scrollarea;
HDC hdc;

if(typewriter_select_active>0)
{
  typewriter_select_active=0;
  typewriter_update(0,LINEBUFFER-1,0,PAPERWIDTH-1);
}

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;

scrollarea.left = typewriter_CursorX(0);
scrollarea.right = typewriter_CursorX(PAPERWIDTH)-1;
scrollarea.top = typewriter_CursorY(1);
scrollarea.bottom = typewriter_CursorY(LINEBUFFER)-1;
if(debug&DEBUGinterface) fprintf(debug_fh, "scrollarea: left: %d right: %d top: %d bottom: %d\n",
   scrollarea.left, scrollarea.right, scrollarea.top, scrollarea.bottom);
ScrollWindowEx(typewriterhwnd, 0, -font_height,
	       &scrollarea, NULL, NULL, NULL, SW_INVALIDATE);
/* UpdateWindow(typewriterhwnd); */
/*
hdc = GetDC(typewriterhwnd);
BitBlt(hdc, typewriter_CursorX(0), typewriter_CursorY(0),
	    font_width*PAPERWIDTH, font_height*(LINEBUFFER-1),
       hdc, typewriter_CursorX(0), typewriter_CursorY(1), SRCCOPY);
BitBlt(hdc, typewriter_CursorX(0), typewriter_CursorY(LINEBUFFER-1),
	    font_width*PAPERWIDTH, font_height,
       hdc, 0, 0, WHITENESS);
ReleaseDC(typewriterhwnd, hdc);
*/
}

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;
}

#ifdef TYPEWRITER_SOUND
static void typewriter_sound_flush()
{
int iwait;
if(ipnt>0)
{
  whdr[ibuffer].dwBufferLength = ipnt;
  if(debug&DEBUGinterface) fprintf(debug_fh, "play_typewriter_flush. waveOutWrite buffer %d, len: %d\n", ibuffer, ipnt);
  waveOutWrite(hwo, &whdr[ibuffer], sizeof(whdr[ibuffer]));
  ibuffer++;
  if(ibuffer>=nbuffers) ibuffer=0;
  iwait=0;
  for(iwait=0; !(whdr[ibuffer].dwFlags&WHDR_DONE); iwait++) Sleep(0);
  if(debug&DEBUGinterface) fprintf(debug_fh, "play_typewriter_flush. Wait for buffer %d: iwait: %d\n", ibuffer, iwait);
  ipnt=0;
}
}

static void play_typewriter_sound(short *sound, int sound_len)
{
int i;
char *bsound=(char *) sound;
if(debug&DEBUGinterface) fprintf(debug_fh, "play_typewriter_sound called. Len=%d\n", sound_len);
for(i=0;i<sound_len;i++)
{
  /*
  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "play_typewriter_sound sample %d, ipnt: %d, ibuffer: %d\n", i, ipnt, ibuffer);
    fflush(debug_fh);
  }
  */
  sbuffers[ibuffer][ipnt++] = *bsound++;
  if(ipnt>=buffersize) typewriter_sound_flush();
}
/* while(ipnt<buffersize) sbuffers[ibuffer][ipnt++] = 0; */
typewriter_sound_flush();
}
#endif

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

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

#ifdef TYPEWRITER_SOUND
  if(sound_enabled)
  {
    if(c == 0)
    {
      play_typewriter_sound(typewriter_sound_spaces[typewriter_sound_spaces_count++], typewriter_sound_space1_len);
      if(typewriter_sound_spaces[typewriter_sound_spaces_count]==NULL) typewriter_sound_spaces_count=0;
    }
    else if(c==58)
    {
      play_typewriter_sound(typewriter_sound_lc, typewriter_sound_lc_len);
    }
    else if(c==60)
    {
      play_typewriter_sound(typewriter_sound_uc, typewriter_sound_uc_len);
    }
    else if(c != 29 && c != 62 && c != 63)
    {
      play_typewriter_sound(typewriter_sound_typers[typewriter_sound_typers_count++], typewriter_sound_typer1_len);
      if(typewriter_sound_typers[typewriter_sound_typers_count]==NULL) typewriter_sound_typers_count=0;
    }
    waveOutReset(hwo);
  }
#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==(unsigned char) '£')
      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(typewriterhwnd == NULL) return;
  typewriter_drawchar(cur_row-scrollpos,this_col);
}
if(demomode==3)
{
  ccase = c;
  if(typewriter_uppercase) ccase+=128;
  if(ccase==demochar)
  {
    demomode=1;
    GIERdemocommand();
  }
}
}

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

reader_modified = True;

if(debug&DEBUGinterface) fprintf(debug_fh, "reader_nchars: %d\n", reader_nchars);
if(reader_nchars == READER_TAPE_WINDOW+READER_TAPE_OFFSET) return;
if(debug&DEBUGinterface) fprintf(debug_fh, "reader_current_fd: %d\n", reader_current_fd);
if(reader_current_fd == -1) return;

if(reader_filetype == 0)
{
  nread=read(reader_current_fd, reader_char_table+reader_nchars, READER_TAPE_WINDOW+READER_TAPE_OFFSET-reader_nchars);

  if(nread <= 0)
  {
    close(reader_current_fd);
    reader_current_fd = -1;
    nread=0;
  }
  reader_nchars += nread;
}
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_update()
{
HDC hdc;
HPEN hpen, hpenold;
HBRUSH hbr, hbrold;
int i,j,k;
char txt[READER_TAPE_WINDOW+READER_TAPE_OFFSET+1];
unsigned char c;
int mask;
int local_uppercase;

if(readerhwnd == NULL) return;

if(!reader_modified) return;

txt[READER_TAPE_WINDOW] = '\0';

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

hdc = GetDC(readerhwnd);

oldfont=SelectObject(hdc, font);

SetTextColor(hdc, RGB(0,0,0));
SetBkColor(hdc, RGB(255,255,255));
for(j=0; j<READER_TAPE_WINDOW+READER_TAPE_OFFSET; j++) txt[j] = ' ';
TextOut(hdc, 0, 0, txt, READER_TAPE_WINDOW+READER_TAPE_OFFSET);
TextOut(hdc, 0, 0, reader_filename, strlen(reader_filename));

for(j=0; j<READER_TAPE_WINDOW+READER_TAPE_OFFSET; j++)
{
  if((j-READER_TAPE_OFFSET)>=reader_nchars)
  {
    c = ' ';
  }
  else
  {
    local_uppercase = 0;
    c = flx2a(remove_parity((unsigned char) reader_char_table[j]), &local_uppercase);
    if(c==255) c=' ';
  }
  txt[j] = c;
}
SetBkColor(hdc, RGB(128,128,128));
TextOut(hdc, 0, 1*font_height, txt, READER_TAPE_WINDOW+READER_TAPE_OFFSET);

mask = 1;
SetBkColor(hdc, RGB(255,255,255));
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;
  }
  TextOut(hdc, 0, (10-i)*font_height, txt, READER_TAPE_WINDOW+READER_TAPE_OFFSET);
  if(debug&DEBUGinterface) fprintf(debug_fh, "Print: %s\n", txt);
}
SetBkColor(hdc, RGB(128,128,128));

for(j=0; j<READER_TAPE_WINDOW+READER_TAPE_OFFSET; j++) txt[j]=' ';
txt[READER_TAPE_OFFSET] = '^';
TextOut(hdc, 0, 11*font_height, txt, READER_TAPE_WINDOW+READER_TAPE_OFFSET);

SelectObject(hdc, oldfont);
ReleaseDC(readerhwnd, hdc);
reader_modified = False;
}

LRESULT CALLBACK readerWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
HDC hdcBitmap;
int i;
OPENFILENAME openfile;
int status;

PAINTSTRUCT ps;
switch(message)
{
case WM_COMMAND:
  switch(LOWORD(wParam))
  {
  case IDM_READER_RESET:
    reader_nchars=READER_TAPE_OFFSET;
    reader_uppercase = 0;
    reader_nsavedchars = 0;
    reader_linepos=0;
    for(i=0; i<READER_TAPE_OFFSET; i++) reader_char_table[i]=0;
    /* bump into READ: */
  case IDM_READER_READ:
    openfile.lStructSize = sizeof(openfile);
    openfile.hwndOwner = readerhwnd;
    openfile.lpstrFilter = "Flexowriter or ASCII files (*.flx,*.asc)\0*.flx;*.asc\0ASCII files (*.asc)\0*.asc\0Flexowriter files (*.flx)\0*.flx\0All files (*.*)\0*.*\0\0";
    openfile.lpstrCustomFilter = NULL;
    openfile.nMaxCustFilter = 0;
    openfile.nFilterIndex = 0;
    openfile.lpstrFile = reader_filename;
    openfile.nMaxFile = sizeof(reader_filename)-1;
    openfile.lpstrFileTitle = NULL;
    openfile.nMaxFileTitle = 0;
    openfile.lpstrInitialDir = NULL;
    openfile.lpstrTitle = NULL;
    openfile.Flags = OFN_EXPLORER;
    openfile.nFileOffset = 0;
    openfile.nFileExtension = 0;
    openfile.lpstrDefExt = "flx";
    openfile.lCustData = 0;
    openfile.lpfnHook = NULL;
    openfile.lpTemplateName = NULL;
    status=GetOpenFileName(&openfile);
    if(status != 0)
    {
      if(debug&DEBUGinterface)
      {
	fprintf(debug_fh, "Opening file: %s\n", reader_filename);
	fprintf(debug_fh, "nFileExtension: %d\n", openfile.nFileExtension);
      }
      if(!strncmp(reader_filename+openfile.nFileExtension, "flx", 3))
      {
	reader_current_fd = open(reader_filename, O_RDONLY|O_BINARY);
	reader_filetype = 0;
      }
      else
      {
	reader_current_fd = open(reader_filename, O_RDONLY);
	reader_filetype = 1;
      }
      reader_update_buffer();
      reader_update();
      YE_wait=0;
    }
    else if(debug&DEBUGinterface)
    {
      fprintf(debug_fh, "status from GetOpenFileName: %d\n", status);
      fprintf(debug_fh, "error: %ld\n", CommDlgExtendedError());
    }
    break;
  case IDM_READER_SKIP:
    break;
  case IDM_READER_UP:
    break;
  }
  break;
case WM_PAINT:
  hdc = BeginPaint(hWnd, &ps);
  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "reader WM_PAINT.\n");
    fprintf(debug_fh, "left: %d, right: %d, top: %d, bottom: %d\n",
	   ps.rcPaint.left, ps.rcPaint.right, ps.rcPaint.top, ps.rcPaint.bottom);
  }
  EndPaint(hWnd, &ps);
  reader_modified = True;
  reader_update();
  break;
case WM_DESTROY:
  readerhwnd = NULL;
  update_viewmenu();
  break;
default:
  return(DefWindowProc(hWnd, message, wParam, lParam));
}
return 0;
}

static BOOL readerInitApplication(HINSTANCE hInstance)
{
WNDCLASS wc;
BOOL rc;

wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = readerWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "READERMENU";
wc.lpszClassName = "GIERreader";

rc=RegisterClass(&wc);
return rc;
}

static BOOL readerInitInstance(HINSTANCE hinstance, INT ncmdshow)
{
RECT warea;

warea.left = 0;
warea.right = font_width*(READER_TAPE_WINDOW+READER_TAPE_OFFSET)-1;
warea.top = 0;
warea.bottom = 12*font_height;
AdjustWindowRect(&warea, WS_OVERLAPPEDWINDOW, True);

readerhwnd = CreateWindow("GIERreader", "GIER Paper tape reader", WS_OVERLAPPEDWINDOW,
       25, 80, warea.right-warea.left+1, warea.bottom-warea.top+1,
       (HWND)NULL,(HMENU)NULL, hinstance, (LPSTR)NULL);

if(readerhwnd == NULL) return 0;

ShowWindow(readerhwnd, ncmdshow);
UpdateWindow(readerhwnd);

return 1;
}

reader_init()
{
int i;
double x;
readerInitApplication(mainhInstance);
readerInitInstance(mainhInstance, SW_SHOWNORMAL);
update_viewmenu();
}

reader_destroy()
{
if(readerhwnd != NULL)
{
  DestroyWindow(readerhwnd);
  readerhwnd=NULL;
  update_viewmenu();
}
}

BOOL reader_is_visible()
{
return readerhwnd != NULL;
}


void reader_wait(int status)
{
if(debug&DEBUGinterface) fprintf(debug_fh, "reader_wait: %d reader_nchars: %d reader_offset: %d\n", status, reader_nchars, reader_offset);
if(status)
{
  YE_wait=1;
}
else
{
  YE_wait=0;
  reader_update_buffer();
}
}

void punch_update()
{
HDC hdc;
HPEN hpen, hpenold;
HBRUSH hbr, hbrold;
int i,j,k;
char txt[PUNCH_TAPE_WINDOW+1];
unsigned char c;
int mask;
int punch_uppercase;

if(punchhwnd == NULL) return;

hdc = GetDC(punchhwnd);

oldfont=SelectObject(hdc, font);
SetTextColor(hdc, RGB(0,0,0));
SetBkColor(hdc, RGB(255,255,255));
for(j=0; j<PUNCH_TAPE_WINDOW; j++) txt[j]=' ';
TextOut(hdc, 0, 0, txt, PUNCH_TAPE_WINDOW);
TextOut(hdc, 0, 0, punch_filename, strlen(punch_filename));
SetBkColor(hdc, RGB(128,128,128));

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;
}
TextOut(hdc, 0, 1*font_height, txt, PUNCH_TAPE_WINDOW);

SetBkColor(hdc, RGB(255,255,255));
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;
  }
  TextOut(hdc, 0, font_height*(10-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] = '^';
SetBkColor(hdc, RGB(128,128,128));
TextOut(hdc, 0, font_height*11, txt, PUNCH_TAPE_WINDOW);
SelectObject(hdc, oldfont);
ReleaseDC(punchhwnd, hdc);
}

LRESULT CALLBACK punchWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
int i;
OPENFILENAME openfile;
int status;

PAINTSTRUCT ps;
switch(message)
{
case WM_COMMAND:
  if(debug&DEBUGinterface) fprintf(debug_fh, "readerWndProc. WM_COMMAND.\n");
  switch(LOWORD(wParam))
  {
  case IDM_PUNCH_ENDTAPE:
    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;
      strcpy(punch_filename, "- no tape -");
    }
    punch_update();
    break;
  case IDM_PUNCH_NEWTAPE:
    punch_filename[0] = '\0';
    openfile.lStructSize = sizeof(openfile);
    openfile.hwndOwner = punchhwnd;
    openfile.lpstrFilter = "Flexowriter or ASCII files (*.flx,*.asc)\0*.flx;*.asc\0ASCII files (*.asc)\0*.asc\0Flexowriter files (*.flx)\0*.flx\0All files (*.*)\0*.*\0\0";
    openfile.lpstrCustomFilter = NULL;
    openfile.nMaxCustFilter = 0;
    openfile.nFilterIndex = 0;
    openfile.lpstrFile = punch_filename;
    openfile.nMaxFile = sizeof(punch_filename)-1;
    openfile.lpstrFileTitle = NULL;
    openfile.nMaxFileTitle = 0;
    openfile.lpstrInitialDir = NULL;
    openfile.lpstrTitle = NULL;
    openfile.Flags = OFN_EXPLORER;
    openfile.nFileOffset = 0;
    openfile.nFileExtension = 0;
    openfile.lpstrDefExt = "flx";
    openfile.lCustData = 0;
    openfile.lpfnHook = NULL;
    openfile.lpTemplateName = NULL;
    status=GetSaveFileName(&openfile);
    if(status != 0)
    {
      if(punch_current_fd != -1) close(punch_current_fd);
      if(debug&DEBUGinterface) fprintf(debug_fh, "Opening file: %s\n", punch_filename);
      if(!strncmp(punch_filename+openfile.nFileExtension, "flx", 3))
      {
	punch_current_fd = open(punch_filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
	punch_filetype = 0;
      }
      else
      {
	punch_current_fd = open(punch_filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
	punch_filetype = 1;
	punch_uppercase = 0;
      }
      if(punch_current_fd == -1) strcpy(punch_filename, "- no tape -");
      punch_update();
    }
    break;
  }
  break;
case WM_PAINT:
  hdc = BeginPaint(hWnd, &ps);
  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "punch WM_PAINT.\n");
    fprintf(debug_fh, "left: %d, right: %d, top: %d, bottom: %d\n",
	   ps.rcPaint.left, ps.rcPaint.right, ps.rcPaint.top, ps.rcPaint.bottom);
  }
  EndPaint(hWnd, &ps);
  punch_update();
  break;
case WM_DESTROY:
  punchhwnd = NULL;
  update_viewmenu();
  break;
default:
  return(DefWindowProc(hWnd, message, wParam, lParam));
}
return 0;
}

static BOOL punchInitApplication(HINSTANCE hInstance)
{
WNDCLASS wc;
BOOL rc;

wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = punchWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "PUNCHMENU";
wc.lpszClassName = "GIERpunch";

rc=RegisterClass(&wc);
return rc;
}

static BOOL punchInitInstance(HINSTANCE hinstance, INT ncmdshow)
{
RECT warea;

warea.left = 0;
warea.right = font_width*PUNCH_TAPE_WINDOW-1;
warea.top = 0;
warea.bottom = 12*font_height;
AdjustWindowRect(&warea, WS_OVERLAPPEDWINDOW, True);

punchhwnd = CreateWindow("GIERpunch", "GIER Paper tape punch", WS_OVERLAPPEDWINDOW,
       290, 80, warea.right-warea.left+1, warea.bottom-warea.top+1,
       (HWND)NULL,(HMENU)NULL, hinstance, (LPSTR)NULL);

if(punchhwnd == NULL) return 0;

ShowWindow(punchhwnd, ncmdshow);
UpdateWindow(punchhwnd);

return 1;
}

punch_init()
{
int i;
double x;
strcpy(punch_filename, "- no tape -");
punchInitApplication(mainhInstance);
punchInitInstance(mainhInstance, SW_SHOWNORMAL);
update_viewmenu();
}

punch_destroy()
{
if(punchhwnd != NULL)
{
  DestroyWindow(punchhwnd);
  punchhwnd=NULL;
  update_viewmenu();
}
}

BOOL punch_is_visible()
{
  return punchhwnd != NULL;
}

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(punchhwnd == NULL) punch_init();
    SendMessage(punchhwnd, WM_COMMAND, IDM_PUNCH_NEWTAPE, 0);
  }

  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;
    strcpy(punch_filename, "- 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 dump_pdlg(PAGESETUPDLG *pdlg)
{
  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "Dump of contents in PAGESETUPDLG:\n");
    fprintf(debug_fh, "Flags: %ld:", pdlg->Flags);
    if(pdlg->Flags&PSD_MINMARGINS) fprintf(debug_fh, " PSD_MINMARGINS");
    if(pdlg->Flags&PSD_MARGINS) fprintf(debug_fh, " PSD_MARGINS");
    if(pdlg->Flags&PSD_INTHOUSANDTHSOFINCHES) fprintf(debug_fh, " PSD_INTHOUSANDTHSOFINCHES");
    if(pdlg->Flags&PSD_INHUNDREDTHSOFMILLIMETERS) fprintf(debug_fh, " PSD_INHUNDREDTHSOFMILLIMETERS");
    if(pdlg->Flags&PSD_DISABLEMARGINS) fprintf(debug_fh, " PSD_DISABLEMARGINS");
    if(pdlg->Flags&PSD_DISABLEPRINTER) fprintf(debug_fh, " PSD_DISABLEPRINTER");
    if(pdlg->Flags&PSD_NOWARNING) fprintf(debug_fh, " PSD_NOWARNING");
    if(pdlg->Flags&PSD_DISABLEORIENTATION) fprintf(debug_fh, " PSD_DISABLEORIENTATION");
    if(pdlg->Flags&PSD_DISABLEPAPER) fprintf(debug_fh, " PSD_DISABLEPAPER");
    if(pdlg->Flags&PSD_RETURNDEFAULT) fprintf(debug_fh, " PSD_RETURNDEFAULT");
    if(pdlg->Flags&PSD_SHOWHELP) fprintf(debug_fh, " PSD_SHOWHELP");
    if(pdlg->Flags&PSD_ENABLEPAGESETUPHOOK) fprintf(debug_fh, " PSD_ENABLEPAGESETUPHOOK");
    if(pdlg->Flags&PSD_ENABLEPAGESETUPTEMPLATE) fprintf(debug_fh, " PSD_ENABLEPAGESETUPTEMPLATE");
    if(pdlg->Flags&PSD_ENABLEPAGESETUPTEMPLATEHANDLE) fprintf(debug_fh, " PSD_ENABLEPAGESETUPTEMPLATEHANDLE");
    if(pdlg->Flags&PSD_ENABLEPAGEPAINTHOOK) fprintf(debug_fh, " PSD_ENABLEPAGEPAINTHOOK");
    if(pdlg->Flags&PSD_DISABLEPAGEPAINTING) fprintf(debug_fh, " PSD_DISABLEPAGEPAINTING");
    fprintf(debug_fh, "\n");
    fprintf(debug_fh, "ptPaperSize: x: %d y: %d\n", pdlg->ptPaperSize.x, pdlg->ptPaperSize.y);
    fprintf(debug_fh, "rtMinMargin: left: %d top: %d right: %d bottom: %d\n",
            pdlg->rtMinMargin.left, pdlg->rtMinMargin.top, pdlg->rtMinMargin.right, pdlg->rtMinMargin.bottom);
    fprintf(debug_fh, "rtMargin: left: %d top: %d right: %d bottom: %d\n",
            pdlg->rtMargin.left, pdlg->rtMargin.top, pdlg->rtMargin.right, pdlg->rtMargin.bottom);
  }
}

BOOL CALLBACK PrintDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  LPDEVNAMES lpdevnames;
  PAGESETUPDLG pdlg;
  switch(message)
  {
  case WM_INITDIALOG:
    lpdevnames = (LPDEVNAMES) GlobalLock(printer_hDevNames);
    if(debug&DEBUGinterface) fprintf(debug_fh, "Setting IDM_PRINT_NAME to: '%s'.\n", ((char *) lpdevnames)+lpdevnames->wDeviceOffset);
    SetDlgItemText(hWnd, IDM_PRINT_NAME, ((char *) lpdevnames)+lpdevnames->wDeviceOffset);
    SetDlgItemInt(hWnd, IDM_PRINT_FONTSIZE, printer_fontsize, FALSE);
    SetDlgItemInt(hWnd, IDM_PRINT_LPI, printer_lpi, FALSE);
    SetDlgItemInt(hWnd, IDM_PRINT_LPP, printer_lines, FALSE);
    set_check(GetDlgItem(hWnd, IDM_PRINT_NOADVANCE), printer_uadvance);
    GlobalUnlock(lpdevnames);
    break;
  case WM_COMMAND:
    switch(LOWORD(wParam))
    {
    case IDM_PRINT_OK:
      EndDialog(hWnd, IDM_PRINT_OK);
      return TRUE;
    case IDM_PRINT_CANCEL:
      EndDialog(hWnd, IDM_PRINT_CANCEL);
      return TRUE;
    case IDM_PRINT_SELECT:
      memset(&pdlg, 0, sizeof(pdlg));
      pdlg.lStructSize = sizeof(pdlg);
      pdlg.hwndOwner = mainhwnd;
      pdlg.hDevMode = printer_hDevMode;
      pdlg.hDevNames = printer_hDevNames;
      pdlg.rtMargin.top = printer_lastMargin.top;
      pdlg.rtMargin.bottom = printer_lastMargin.bottom;
      pdlg.rtMargin.left = printer_lastMargin.left;
      pdlg.rtMargin.right = printer_lastMargin.right;
      pdlg.Flags = PSD_MARGINS|printer_marginunits;
      if(debug&DEBUGinterface) fprintf(debug_fh, "Calling PageSetupDlg to modify printer setup.\n");
      PageSetupDlg(&pdlg);
      dump_pdlg(&pdlg);
      if(debug&DEBUGinterface)
      {
        fprintf(debug_fh, "Old printer_hDevMode: %ld\n", printer_hDevMode);
        fprintf(debug_fh, "Old printer_hDevNames: %ld\n", printer_hDevNames);
        fprintf(debug_fh, "New printer_hDevMode: %ld\n", pdlg.hDevMode);
        fprintf(debug_fh, "New printer_hDevNames: %ld\n", pdlg.hDevNames);
	fprintf(debug_fh, "Margins: top: %d bottom: %d left: %d right: %d\n",
	pdlg.rtMargin.top, pdlg.rtMargin.bottom, pdlg.rtMargin.left, pdlg.rtMargin.right);
      }
      printer_hDevMode = pdlg.hDevMode;
      printer_hDevNames = pdlg.hDevNames;
      printer_lastMargin.top = pdlg.rtMargin.top;
      printer_lastMargin.bottom = pdlg.rtMargin.bottom;
      printer_lastMargin.left = pdlg.rtMargin.left;
      printer_lastMargin.right = pdlg.rtMargin.right;
      printer_marginunits = pdlg.Flags&(PSD_INTHOUSANDTHSOFINCHES|PSD_INHUNDREDTHSOFMILLIMETERS);
      lpdevnames = (LPDEVNAMES) GlobalLock(printer_hDevNames);
      if(debug&DEBUGinterface) fprintf(debug_fh, "Setting IDM_PRINT_NAME to: '%s'.\n", ((char *) lpdevnames)+lpdevnames->wDeviceOffset);
      SetDlgItemText(hWnd, IDM_PRINT_NAME, ((char *) lpdevnames)+lpdevnames->wDeviceOffset);
      GlobalUnlock(lpdevnames);
      break;
    case IDM_PRINT_LPI:
      printer_lpi = GetDlgItemInt(hWnd, IDM_PRINT_LPI, NULL, FALSE);
      if(debug&DEBUGinterface) fprintf(debug_fh, "Setting printer_lpi to: %d\n", printer_lpi);
      break;
    case IDM_PRINT_FONTSIZE:
      printer_fontsize = GetDlgItemInt(hWnd, IDM_PRINT_FONTSIZE, NULL, FALSE);
      if(debug&DEBUGinterface) fprintf(debug_fh, "Setting printer_fontsize to: %d\n", printer_fontsize);
      break;
    case IDM_PRINT_LPP:
      printer_lines = GetDlgItemInt(hWnd, IDM_PRINT_LPP, NULL, FALSE);
      if(debug&DEBUGinterface) fprintf(debug_fh, "Setting printer_lines to: %d\n", printer_lines);
      break;
    case IDM_PRINT_NOADVANCE:
      printer_uadvance=get_check(GetDlgItem(hWnd, IDM_PRINT_NOADVANCE));
      if(debug&DEBUGinterface) fprintf(debug_fh, "Setting printer_uadvance to: %d\n", printer_uadvance);
      break;
    default:
      return TRUE;
    }
  default:
    return FALSE;
  }
  return FALSE;
}

static void printer_update_status()
{
  if(printerhwnd != NULL)
  {
    if(printer_pages == 0)
    {
      EnableMenuItem(GetMenu(printerhwnd), IDM_PRINT_SETUP, MF_BYCOMMAND | MF_ENABLED);
      EnableMenuItem(GetMenu(printerhwnd), IDM_PRINT_STARTPRINT, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
      EnableMenuItem(GetMenu(printerhwnd), IDM_PRINT_CANCELPRINT, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
    }
    else
    {
      EnableMenuItem(GetMenu(printerhwnd), IDM_PRINT_SETUP, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
      EnableMenuItem(GetMenu(printerhwnd), IDM_PRINT_STARTPRINT, MF_BYCOMMAND | MF_ENABLED);
      EnableMenuItem(GetMenu(printerhwnd), IDM_PRINT_CANCELPRINT, MF_BYCOMMAND | MF_ENABLED);
    }
    DrawMenuBar(printerhwnd);
  }
}

void printer_update()
{
  HDC hdc;
  HPEN hpen, hpenold;
  int i;
  char txt[PRINTER_WINDOW+1];

  if(printerhwnd == NULL) return;

  hdc = GetDC(printerhwnd);

  oldfont=SelectObject(hdc, font);
  SetTextColor(hdc, RGB(0,0,0));
  SetBkColor(hdc, RGB(255,255,255));
  for(i=0; i<PRINTER_WINDOW; i++) txt[i]=' ';
  TextOut(hdc, 0, 0, txt, PRINTER_WINDOW);
  if(printer_pages == 0)
  {
    sprintf(txt, "Print buffer empty");
  }
  else
  {
    sprintf(txt, "Page %d, line: %d", printer_pages, printer_lineno);
  }
  TextOut(hdc, 0, 0, txt, strlen(txt));
  SetBkColor(hdc, RGB(128,128,128));

  SelectObject(hdc, oldfont);
  ReleaseDC(printerhwnd, hdc);
}

static printer_release_storage()
{
  PRINTERPAGE *currentpage, *nextpage;
  PRINTERLINE *currentline, *nextline;

  currentpage = firstpage;
  while(currentpage != NULL)
  {
    currentline = currentpage->firstline;
    while(currentline != NULL)
    {
      if(debug&DEBUGinterface) {fprintf(debug_fh, "free oneline(%d) at %8.8x\n", currentline->linelength, (int) currentline->oneline); fflush(debug_fh);}
      free(currentline->oneline);
      nextline = currentline->nextline;
      if(debug&DEBUGinterface) {fprintf(debug_fh, "free currentline at %8.8x\n", (int) currentline); fflush(debug_fh);}
      free((char *) currentline);
      currentline = nextline;
    }
    nextpage = currentpage->nextpage;
    if(debug&DEBUGinterface) {fprintf(debug_fh, "free currentpage at %8.8x\n", (int) currentpage); fflush(debug_fh);}
    free((char *) currentpage);
    currentpage = nextpage;
  }
  firstpage = NULL;
  currentpage = NULL;
  printer_pages = 0;
  printer_lineno = 0;
  printer_update();
  printer_update_status();
}

static printer_print_storage(HWND hWnd)
{
  PRINTDLG pd;
  HDC hdc;
  DOCINFO di;
  BOOL bSuccess = TRUE;
  BOOL bUserAbort = FALSE;
  PRINTERPAGE *currentpage;
  PRINTERLINE *currentline;
  int iPhysWidth, iPhysHeight, iPhysOffsetX, iPhysOffsetY;
  int mleft, mtop, mright, mbottom;
  int iLeftAdjust, iTopAdjust, iRightAdjust, iBottomAdjust;
  int fontsize, small_fontsize;
  TEXTMETRIC tm;
  HFONT printer_font, printer_symbol_font, printer_small_font;
  HANDLE hold;
  int iColCopy, iNonColCopy, iPage, iLine, iCol, iChar, redtext;
  int printer_case;
  unsigned char c,c2,c3;
  int xChar, yChar, xpos, ypos;
  HBRUSH hbr, hbrold;
  int oldmode;

  if(debug&DEBUGinterface) fprintf(debug_fh, "printer_print_storage started.\n");
  memset(&pd, 0, sizeof(pd));
  pd.lStructSize = sizeof(pd);
  pd.hwndOwner = hWnd;
  pd.hDevMode = printer_hDevMode;
  pd.hDevNames = printer_hDevNames;
  pd.nMinPage = 1;
  pd.nMaxPage = printer_pages;
  pd.nFromPage = 1;
  pd.nToPage = printer_pages;
  pd.Flags = PD_RETURNDC;
  if(PrintDlg(&pd))
  {
    if(debug&DEBUGinterface)
    {
      fprintf(debug_fh, "After PrintDlg.\n");
      fprintf(debug_fh, "Flags: %ld\n", pd.Flags);
      fprintf(debug_fh, "nFromPage: %d\n", pd.nFromPage);
      fprintf(debug_fh, "nToPage: %d\n", pd.nToPage);
    }
    printer_hDevMode = pd.hDevMode;
    printer_hDevNames = pd.hDevNames;
    hdc = pd.hDC;
    mleft = MulDiv(printer_lastMargin.left, GetDeviceCaps(hdc, LOGPIXELSX), 1000);
    mtop = MulDiv(printer_lastMargin.top, GetDeviceCaps(hdc, LOGPIXELSY), 1000);
    mright = MulDiv(printer_lastMargin.right, GetDeviceCaps(hdc, LOGPIXELSX), 1000);
    mbottom = MulDiv(printer_lastMargin.bottom, GetDeviceCaps(hdc, LOGPIXELSY), 1000);
    iPhysOffsetX = GetDeviceCaps(hdc, PHYSICALOFFSETX);
    iPhysOffsetY = GetDeviceCaps(hdc, PHYSICALOFFSETY);
    iPhysWidth = GetDeviceCaps(hdc, PHYSICALWIDTH);
    iPhysHeight = GetDeviceCaps(hdc, PHYSICALHEIGHT);
    iLeftAdjust = mleft - iPhysOffsetX;
    iTopAdjust = mtop - iPhysOffsetY;
    iRightAdjust = mright - (iPhysWidth - iPhysOffsetX - GetDeviceCaps(hdc, HORZRES));
    iBottomAdjust = mbottom - (iPhysHeight - iPhysOffsetY - GetDeviceCaps(hdc, VERTRES));
    fontsize = -MulDiv(printer_fontsize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
    small_fontsize = fontsize/2;

    SetMapMode(hdc, MM_TEXT);
    printer_font = CreateFont(fontsize,0,0,0,400,0,0,0,ANSI_CHARSET,OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,
		      PROOF_QUALITY,FIXED_PITCH|FF_MODERN,"Courier New");
    printer_small_font = CreateFont(small_fontsize,0,0,0,400,0,0,0,ANSI_CHARSET,OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,
			PROOF_QUALITY,FIXED_PITCH|FF_MODERN,"Courier New");
    printer_symbol_font = CreateFont(fontsize,0,0,0,400,0,0,0,SYMBOL_CHARSET,OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,
			PROOF_QUALITY,FIXED_PITCH|FF_MODERN,"Symbol");
    
    hold=SelectObject(hdc, printer_font);
    GetTextMetrics(hdc, &tm);
    SelectObject(hdc, hold);
    yChar = tm.tmHeight;
    xChar = tm.tmAveCharWidth;
    if(debug&DEBUGinterface) fprintf(debug_fh, "xChar: %d, yChar: %d\n", xChar, yChar);
    yChar = MulDiv(1, GetDeviceCaps(hdc, LOGPIXELSY), printer_lpi);
    xChar = MulDiv(printer_fontsize, GetDeviceCaps(hdc, LOGPIXELSX), 120);
    if(debug&DEBUGinterface) fprintf(debug_fh, "New: xChar: %d, yChar: %d\n", xChar, yChar);
    memset(&di, 0, sizeof(di));
    di.cbSize = sizeof(di);
    di.lpszDocName = "GIER Printer";
    oldmode=SetBkMode(hdc, TRANSPARENT);
    hbr = GetStockObject(NULL_BRUSH);
    hbrold = SelectObject(hdc, hbr);

    if(StartDoc(hdc, &di)>0)
    {
      for(iColCopy=0; iColCopy<((pd.Flags&PD_COLLATE)?pd.nCopies:1); iColCopy++)
      {
	currentpage = firstpage;
	iPage = 0;
	while(currentpage != NULL)
	{
	  iPage++;
	  if((!(pd.Flags&PD_PAGENUMS)) ||
	     (iPage>=pd.nFromPage && iPage<=pd.nToPage))
	  {
	    for(iNonColCopy = 0; iNonColCopy < ((pd.Flags&PD_COLLATE)?1:pd.nCopies); iNonColCopy++)
	    {
	      if(StartPage(hdc)<0)
	      {
		bSuccess = FALSE;
		break;
	      }
	      SetViewportOrgEx(hdc, iLeftAdjust, iTopAdjust, NULL);
	      currentline = currentpage->firstline;
	      printer_case = currentpage->firstcase;
	      redtext = currentpage->firstred;
	      SetTextColor(hdc, RGB(redtext?255:0,0,0));
	      iLine = 0;
	      while(currentline != NULL)
	      {
		iCol = 0;
		xpos = 0;
		ypos = yChar*iLine;
		for(iChar=0; iChar<currentline->linelength; iChar++)
		{
		  c = currentline->oneline[iChar];
		  if(c == 64)
		  {
		    iLine++;
		  }
		  else if(c == 80)
		  {
		  }
		  else if(c == 72)
		  {
		  }
		  else if(c == 29)
		  {
		    redtext = 1;
		    SetTextColor(hdc, RGB(255,0,0));
		  }
		  else if(c == 62)
		  {
		    redtext = 0;
		    SetTextColor(hdc, RGB(0,0,0));
		  }
		  else
		  {
		    c2 = flx2a(c, &printer_case);
		    if(c2 != 255)
		    {
		      if(c2 == (unsigned char)'£')
		      {
			hold=SelectObject(hdc, printer_symbol_font);
			c3 = 218;
			TextOut(hdc, xpos, ypos, &c3, 1);
			SelectObject(hdc, hold);
		      }
		      else if(c2 == '*')
		      {
			hold=SelectObject(hdc, printer_symbol_font);
			c3 = 180;
			TextOut(hdc, xpos, ypos, &c3, 1);
			SelectObject(hdc, hold);
		      }
		      else if(c2 == '&')
		      {
			hold=SelectObject(hdc, printer_symbol_font);
			c3 = 217;
			TextOut(hdc, xpos, ypos, &c3, 1);
			SelectObject(hdc, hold);
		      }
		      else if(c2 == '\'')
		      {
			hold=SelectObject(hdc, printer_small_font);
			TextOut(hdc, xpos, ypos+yChar/2, "10", 2);
			SelectObject(hdc, hold);
		      }
		      else
		      {
			hold=SelectObject(hdc, printer_font);
			TextOut(hdc, xpos, ypos, &c2, 1);
			SelectObject(hdc, hold);
		      }
		      if(c != 14 || !printer_uadvance) xpos += xChar;
		    }
		  }
		}
		currentline = currentline->nextline;
	      }
	      if(EndPage(hdc)<0)
	      {
		bSuccess = FALSE;
		break;
	      }
	    }
	  }
	  currentpage = currentpage->nextpage;
	  if(!bSuccess || bUserAbort) break;
	}
	if(!bSuccess || bUserAbort) break;
      }
      EndDoc(hdc);
    }

    SelectObject(hdc, hbrold);
    DeleteObject(hbr);
    SetBkMode(hdc, oldmode);
    DeleteObject(printer_font);
    DeleteObject(printer_small_font);
    DeleteObject(printer_symbol_font);
    DeleteDC(hdc);

    printer_release_storage();
  }
}

LRESULT CALLBACK printerWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;
  int i;
  OPENFILENAME openfile;
  int status;

  PAINTSTRUCT ps;
  switch(message)
  {
  case WM_COMMAND:
    if(debug&DEBUGinterface) fprintf(debug_fh, "printerrWndProc. WM_COMMAND.\n");
    switch(LOWORD(wParam))
    {
    case IDM_PRINT_SETUP:
      if(DialogBox(mainhInstance, "PRINTERWINDOW", mainhwnd, PrintDlgProc)==IDM_PRINT_OK)
      {
        printer_pages=0;
	printer_lineno=0;
	printer_update();
	printer_update_status();
      }
      break;
    case IDM_PRINT_STARTPRINT:
      printer_SY(64);
      printer_print_storage(hWnd);
      break;
    case IDM_PRINT_CANCELPRINT:
      printer_release_storage();
      break;
    }
    break;
  case WM_PAINT:
    hdc = BeginPaint(hWnd, &ps);
    EndPaint(hWnd, &ps);
    printer_update();
    break;
  case WM_DESTROY:
    printerhwnd = NULL;
    update_viewmenu();
    break;
  default:
    return(DefWindowProc(hWnd, message, wParam, lParam));
  }
  return 0;
}

static BOOL printerInitApplication(HINSTANCE hInstance)
{
  WNDCLASS wc;
  BOOL rc;

  wc.style = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc = printerWndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = NULL;
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
  wc.lpszMenuName = "PRINTERMENU";
  wc.lpszClassName = "GIERprinter";

  rc=RegisterClass(&wc);
  return rc;
}

static BOOL printerInitInstance(HINSTANCE hinstance, INT ncmdshow)
{
  RECT warea;

  warea.left = 0;
  warea.right = font_width*PRINTER_WINDOW-1;
  warea.top = 0;
  warea.bottom = 1*font_height;
  AdjustWindowRect(&warea, WS_OVERLAPPEDWINDOW, True);

  printerhwnd = CreateWindow("GIERprinter", "GIER printer", WS_OVERLAPPEDWINDOW,
         25, 325, warea.right-warea.left+1, warea.bottom-warea.top+1,
	 (HWND)NULL,(HMENU)NULL, hinstance, (LPSTR)NULL);

  if(printerhwnd == NULL) return 0;

  ShowWindow(printerhwnd, ncmdshow);
  UpdateWindow(printerhwnd);

  printer_update();
  printer_update_status();

  return 1;
}

static printer_setup1st()
{
  PAGESETUPDLG pdlg;
  LPDEVNAMES lpdevnames;
  /* Set up default printer */
  memset(&pdlg, 0, sizeof(pdlg));
  pdlg.lStructSize = sizeof(pdlg);
  pdlg.hwndOwner = mainhwnd;
  pdlg.Flags = PSD_RETURNDEFAULT|PSD_MARGINS|PSD_INTHOUSANDTHSOFINCHES;
  pdlg.rtMargin.top = 500;
  pdlg.rtMargin.bottom = 500;
  pdlg.rtMargin.left = 500;
  pdlg.rtMargin.right = 500;
  if(debug&DEBUGinterface) fprintf(debug_fh, "Calling PageSetupDlg to get default printer.\n");
  PageSetupDlg(&pdlg);
  printer_lastMargin.top = pdlg.rtMargin.top;
  printer_lastMargin.bottom = pdlg.rtMargin.bottom;
  printer_lastMargin.left = pdlg.rtMargin.left;
  printer_lastMargin.right = pdlg.rtMargin.right;
  printer_marginunits = pdlg.Flags&(PSD_INTHOUSANDTHSOFINCHES|PSD_INHUNDREDTHSOFMILLIMETERS);
  dump_pdlg(&pdlg);
  printer_hDevMode = pdlg.hDevMode;
  printer_hDevNames = pdlg.hDevNames;
  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "Return from PageSetupDlg.\n");
    lpdevnames = (LPDEVNAMES) GlobalLock(printer_hDevNames);
    fprintf(debug_fh, "Device: '%s'\n", ((char *) lpdevnames)+lpdevnames->wDeviceOffset);
    fprintf(debug_fh, "Driver: '%s'\n", ((char *) lpdevnames)+lpdevnames->wDriverOffset);
    fprintf(debug_fh, "Output: '%s'\n", ((char *) lpdevnames)+lpdevnames->wOutputOffset);
    fflush(debug_fh);
    GlobalUnlock(lpdevnames);
  }
  print_has_been_setup=1;
}

printer_init()
{
  int i;
  double x;
  if(!print_has_been_setup) printer_setup1st();
  printerInitApplication(mainhInstance);
  printerInitInstance(mainhInstance, SW_SHOWNORMAL);
  update_viewmenu();
}

printer_destroy()
{
  if(printerhwnd != NULL)
  {
    DestroyWindow(printerhwnd);
    printerhwnd=NULL;
    update_viewmenu();
  }
}

BOOL printer_is_visible()
{
  return printerhwnd != NULL;
}

void printer_SY(char c)
{
  unsigned char uc=(unsigned char) c;
  /*
  	Store character in buffer
  */
  if(printerhwnd == NULL) printer_init();

  if(printerlinelength<MAXPRINTERLINE)
  {
    printerline[printerlinelength++] = uc;
  }

  if(uc == 58) printercase=0;
  else if(uc == 60) printercase=1;
  if(uc == 29) printerred=1;
  else if(uc == 62) printerred=0;

  if(uc == 64 || uc == 72 || uc == 80)
  {
    if(firstpage == NULL)
    {
      firstpage = (PRINTERPAGE *) malloc(sizeof(*firstpage));
      if(debug&DEBUGinterface) {fprintf(debug_fh, "malloc firstpage at: %8.8x\n", (int) firstpage); fflush(debug_fh);}
      currentpage = firstpage;
      currentpage->nextpage = NULL;
      currentpage->firstcase = 0;
      currentpage->firstred = 0;
      currentpage->firstline = NULL;
      currentpage->currentline = NULL;
      printer_pages = 1;
      printer_lineno = 0;
      printer_update_status();
    }
    if(currentpage->currentline == NULL)
    {
      currentpage->currentline = (PRINTERLINE *) malloc(sizeof(*(currentpage->firstline)));
      if(debug&DEBUGinterface) {fprintf(debug_fh, "malloc currentpage->currentline at: %8.8x\n", (int) currentpage->currentline); fflush(debug_fh);}
      currentpage->firstline = currentpage->currentline;
    }
    else
    {
      currentpage->currentline->nextline = (PRINTERLINE *) malloc(sizeof(*(currentpage->firstline)));
      if(debug&DEBUGinterface) {fprintf(debug_fh, "malloc currentpage->currentline->nextline at: %8.8x\n", (int) currentpage->currentline->nextline); fflush(debug_fh);}
      currentpage->currentline = currentpage->currentline->nextline;
      currentpage->currentline->nextline = NULL;
    }
    currentpage->currentline->oneline = malloc(printerlinelength);
    if(debug&DEBUGinterface) {fprintf(debug_fh, "malloc oneline at: %8.8x\n", (int) currentpage->currentline->oneline); fflush(debug_fh);}
    currentpage->currentline->linelength = printerlinelength;
    memcpy(currentpage->currentline->oneline, printerline, printerlinelength);
    printerlinelength = 0;
    if(uc == 64)
    {
      printer_lineno++;
      if(printer_lineno >= printer_lines) uc = 72;
    }
    if(uc == 72)
    {
      /* new page */
      currentpage->nextpage = (PRINTERPAGE *) malloc(sizeof(*firstpage));
      if(debug&DEBUGinterface) {fprintf(debug_fh, "malloc currentpage->nextpage at: %8.8x\n", (int) currentpage->nextpage); fflush(debug_fh);}
      currentpage = currentpage->nextpage;
      currentpage->nextpage = NULL;
      currentpage->firstcase = printercase;
      currentpage->firstred = printerred;
      currentpage->firstline = NULL;
      currentpage->currentline = NULL;
      printer_pages++;
      printer_lineno=0;
    }
    printer_update();
  }
}

BOOL CALLBACK PlotterDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  LPDEVNAMES lpdevnames;
  PAGESETUPDLG pdlg;
  int i;
  switch(message)
  {
  case WM_INITDIALOG:
    lpdevnames = (LPDEVNAMES) GlobalLock(plotter_hDevNames);
    if(debug&DEBUGinterface) fprintf(debug_fh, "Setting IDM_PLOTTER_NAME to: '%s'.\n", ((char *) lpdevnames)+lpdevnames->wDeviceOffset);
    SendMessage(GetDlgItem(hWnd, IDM_PLOTTER_ROTATE), CB_ADDSTRING, 0, (LPARAM) "No");
    SendMessage(GetDlgItem(hWnd, IDM_PLOTTER_ROTATE), CB_ADDSTRING, 0, (LPARAM) "Yes");
    SendMessage(GetDlgItem(hWnd, IDM_PLOTTER_ROTATE), CB_ADDSTRING, 0, (LPARAM) "Auto");
    SetDlgItemText(hWnd, IDM_PLOTTER_NAME, ((char *) lpdevnames)+lpdevnames->wDeviceOffset);
    set_check(GetDlgItem(hWnd, IDM_PLOTTER_CENTER), plotter_center);
    set_check(GetDlgItem(hWnd, IDM_PLOTTER_AUTOUP), plotter_autoup);
    set_check(GetDlgItem(hWnd, IDM_PLOTTER_AUTODOWN), plotter_autodown);
    SendMessage(GetDlgItem(hWnd, IDM_PLOTTER_ROTATE), CB_SETCURSEL, plotter_rotate, (LPARAM) 0);
    GlobalUnlock(lpdevnames);
    break;
  case WM_COMMAND:
    switch(LOWORD(wParam))
    {
    case IDM_PLOTTER_OK:
      EndDialog(hWnd, IDM_PLOTTER_OK);
      return TRUE;
    case IDM_PLOTTER_CANCEL:
      EndDialog(hWnd, IDM_PLOTTER_CANCEL);
      return TRUE;
    case IDM_PLOTTER_SELECT:
      memset(&pdlg, 0, sizeof(pdlg));
      pdlg.lStructSize = sizeof(pdlg);
      pdlg.hwndOwner = mainhwnd;
      pdlg.hDevMode = plotter_hDevMode;
      pdlg.hDevNames = plotter_hDevNames;
      pdlg.rtMargin.top = plotter_lastMargin.top;
      pdlg.rtMargin.bottom = plotter_lastMargin.bottom;
      pdlg.rtMargin.left = plotter_lastMargin.left;
      pdlg.rtMargin.right = plotter_lastMargin.right;
      pdlg.Flags = PSD_MARGINS|plotter_marginunits;
      if(debug&DEBUGinterface) fprintf(debug_fh, "Calling PageSetupDlg to modify plotter setup.\n");
      PageSetupDlg(&pdlg);
      dump_pdlg(&pdlg);
      if(debug&DEBUGinterface)
      {
        fprintf(debug_fh, "Old plotter_hDevMode: %ld\n", plotter_hDevMode);
        fprintf(debug_fh, "Old plotter_hDevNames: %ld\n", plotter_hDevNames);
        fprintf(debug_fh, "New plotter_hDevMode: %ld\n", pdlg.hDevMode);
        fprintf(debug_fh, "New plotter_hDevNames: %ld\n", pdlg.hDevNames);
	fprintf(debug_fh, "Margins: top: %d bottom: %d left: %d right: %d\n",
	pdlg.rtMargin.top, pdlg.rtMargin.bottom, pdlg.rtMargin.left, pdlg.rtMargin.right);
      }
      plotter_hDevMode = pdlg.hDevMode;
      plotter_hDevNames = pdlg.hDevNames;
      plotter_lastMargin.top = pdlg.rtMargin.top;
      plotter_lastMargin.bottom = pdlg.rtMargin.bottom;
      plotter_lastMargin.left = pdlg.rtMargin.left;
      plotter_lastMargin.right = pdlg.rtMargin.right;
      plotter_marginunits = pdlg.Flags&(PSD_INTHOUSANDTHSOFINCHES|PSD_INHUNDREDTHSOFMILLIMETERS);
      lpdevnames = (LPDEVNAMES) GlobalLock(plotter_hDevNames);
      if(debug&DEBUGinterface) fprintf(debug_fh, "Setting IDM_PLOTTER_NAME to: '%s'.\n", ((char *) lpdevnames)+lpdevnames->wDeviceOffset);
      SetDlgItemText(hWnd, IDM_PLOTTER_NAME, ((char *) lpdevnames)+lpdevnames->wDeviceOffset);
      GlobalUnlock(lpdevnames);
      break;
    case IDM_PLOTTER_CENTER:
      plotter_center=get_check(GetDlgItem(hWnd, IDM_PLOTTER_CENTER));
      if(debug&DEBUGinterface) fprintf(debug_fh, "Setting plotter_center to: %d\n", plotter_center);
      break;
    case IDM_PLOTTER_AUTOUP:
      plotter_autoup=get_check(GetDlgItem(hWnd, IDM_PLOTTER_AUTOUP));
      if(debug&DEBUGinterface) fprintf(debug_fh, "Setting plotter_autoup to: %d\n", plotter_autoup);
      break;
    case IDM_PLOTTER_AUTODOWN:
      plotter_autodown=get_check(GetDlgItem(hWnd, IDM_PLOTTER_AUTODOWN));
      if(debug&DEBUGinterface) fprintf(debug_fh, "Setting plotter_autodown to: %d\n", plotter_autodown);
      break;
    case IDM_PLOTTER_ROTATE:
      plotter_rotate=SendMessage(GetDlgItem(hWnd, IDM_PLOTTER_ROTATE), CB_GETCURSEL, 0, 0);
      if(debug&DEBUGinterface) fprintf(debug_fh, "Setting plotter_rotate to: %d\n", plotter_rotate);
      break;
    default:
      return TRUE;
    }
  default:
    return FALSE;
  }
  return FALSE;
}

static void plotter_update_status()
{
  if(plotterhwnd != NULL)
  {
    if(plotter_firstline == NULL)
    {
      EnableMenuItem(GetMenu(plotterhwnd), IDM_PLOTTER_STARTPLOT, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
      EnableMenuItem(GetMenu(plotterhwnd), IDM_PLOTTER_CANCELPLOT, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
    }
    else
    {
      EnableMenuItem(GetMenu(plotterhwnd), IDM_PLOTTER_STARTPLOT, MF_BYCOMMAND | MF_ENABLED);
      EnableMenuItem(GetMenu(plotterhwnd), IDM_PLOTTER_CANCELPLOT, MF_BYCOMMAND | MF_ENABLED);
    }
    DrawMenuBar(plotterhwnd);
  }
}

void plotter_update()
{
  HDC hdc;
  HPEN hpen, hpenold;
  int i;
  char txt[PLOTTER_WINDOW+1];

  if(plotterhwnd == NULL) return;

  hdc = GetDC(plotterhwnd);

  oldfont=SelectObject(hdc, font);
  SetTextColor(hdc, RGB(0,0,0));
  SetBkColor(hdc, RGB(255,255,255));
  for(i=0; i<PLOTTER_WINDOW; i++) txt[i]=' ';
  TextOut(hdc, 0, 0, txt, PLOTTER_WINDOW);
  if(plotter_characters == 0)
  {
    sprintf(txt, "Plotter buffer empty");
  }
  else
  {
    sprintf(txt, "%d bytes", plotter_characters);
  }
  TextOut(hdc, 0, 0, txt, strlen(txt));
  SetBkColor(hdc, RGB(128,128,128));

  SelectObject(hdc, oldfont);
  ReleaseDC(plotterhwnd, hdc);
}

static plotter_release_storage()
{
  PLOTTERLINE *currentline, *nextline;

  currentline = plotter_firstline;
  while(currentline != NULL)
  {
    nextline = currentline->nextline;
    free((char *) currentline);
    currentline = nextline;
  }
  plotter_firstline = NULL;
  plotter_currentline = NULL;
  plotter_characters = 0;
  plotter_xmin=99999999;
  plotter_xmax=-99999999;
  plotter_ymin=99999999;
  plotter_ymax=-99999999;
  plotter_x=0;
  plotter_y=0;
  plotter_update();
  plotter_update_status();
}

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_scale2(PLOTTERMATRIX *a, double scalex, double scaley)
{
  PLOTTERMATRIX m1;
  plotter_coord_init(&m1);
  m1.a[0][0] = scalex;
  m1.a[1][1] = scaley;
  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 plotter_plot_storage(HWND hWnd)
{
  PRINTDLG pd;
  HDC hdc;
  DOCINFO di;
  BOOL bSuccess = TRUE;
  BOOL bUserAbort = FALSE;
  int iPhysWidth, iPhysHeight, iPhysOffsetX, iPhysOffsetY;
  int mleft, mtop, mright, mbottom;
  int iLeftAdjust, iTopAdjust, iRightAdjust, iBottomAdjust;
  HANDLE hold;
  int iColCopy, iNonColCopy, iPage;
  int xpos, ypos;
  HBRUSH hbr, hbrold;
  int oldmode;
  double dev_xmin, dev_xmax, dev_ymin, dev_ymax, dev_xmargin, dev_ymargin;
  double xscale, yscale, scale;
  double dtmp1, dtmp2;
  double shift_x, shift_y;
  char *paper_text;
  PLOTTERLINE *line, *next;
  POINT points[PLOTTERLINE_SIZE+1];
  int do_rotate, i;
  PLOTTERMATRIX m;
  int x,y;
  double unit,xunit,yunit;
  HPEN hpen, hpenold;

  if(debug&DEBUGinterface) fprintf(debug_fh, "plotter_plot started.\n");
  if(debug&DEBUGinterface) fflush(debug_fh);
  memset(&pd, 0, sizeof(pd));
  pd.lStructSize = sizeof(pd);
  pd.hwndOwner = hWnd;
  pd.hDevMode = plotter_hDevMode;
  pd.hDevNames = plotter_hDevNames;
  pd.nMinPage = 1;
  pd.nMaxPage = 1;
  pd.nFromPage = 1;
  pd.nToPage = 1;
  pd.Flags = PD_RETURNDC;
  if(PrintDlg(&pd))
  {
    if(debug&DEBUGinterface)
    {
      fprintf(debug_fh, "After PrintDlg.\n");
      fprintf(debug_fh, "Flags: %ld\n", pd.Flags);
      fprintf(debug_fh, "nFromPage: %d\n", pd.nFromPage);
      fprintf(debug_fh, "nToPage: %d\n", pd.nToPage);
      fflush(debug_fh);
    }
    plotter_hDevMode = pd.hDevMode;
    plotter_hDevNames = pd.hDevNames;
    hdc = pd.hDC;
    if(debug&DEBUGinterface)
    {
      fprintf(debug_fh, "plotter: lastMargin: left: %d right: %d top: %d bottom: %d\n", 
	      plotter_lastMargin.left, plotter_lastMargin.right,
	      plotter_lastMargin.top, plotter_lastMargin.bottom);
    }
    mleft = MulDiv(plotter_lastMargin.left, GetDeviceCaps(hdc, LOGPIXELSX), 1000);
    mtop = MulDiv(plotter_lastMargin.top, GetDeviceCaps(hdc, LOGPIXELSY), 1000);
    mright = MulDiv(plotter_lastMargin.right, GetDeviceCaps(hdc, LOGPIXELSX), 1000);
    mbottom = MulDiv(plotter_lastMargin.bottom, GetDeviceCaps(hdc, LOGPIXELSY), 1000);
    if(debug&DEBUGinterface)
    {
      fprintf(debug_fh, "plotter: mleft: %d mright: %d mtop: %d mbottom: %d\n", 
	      mleft, mright, mtop, mbottom);
      fprintf(debug_fh, "LOGPIXELSX: %d, LOGPIXELSY: %d\n",  GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY));
    }
    iPhysOffsetX = GetDeviceCaps(hdc, PHYSICALOFFSETX);
    iPhysOffsetY = GetDeviceCaps(hdc, PHYSICALOFFSETY);
    if(debug&DEBUGinterface) fprintf(debug_fh, "plotter: iPhysOffset: %d %d\n", iPhysOffsetX, iPhysOffsetY);
    if(debug&DEBUGinterface) fflush(debug_fh);
    iPhysWidth = GetDeviceCaps(hdc, PHYSICALWIDTH);
    iPhysHeight = GetDeviceCaps(hdc, PHYSICALHEIGHT);
    if(debug&DEBUGinterface) fprintf(debug_fh, "plotter: iPhysWidth: %d iPhysHeight: %d\n", iPhysWidth, iPhysHeight);
    if(debug&DEBUGinterface) fflush(debug_fh);
    iLeftAdjust = mleft - iPhysOffsetX;
    iTopAdjust = mtop - iPhysOffsetY;
    iRightAdjust = mright - (iPhysWidth - iPhysOffsetX - GetDeviceCaps(hdc, HORZRES));
    iBottomAdjust = mbottom - (iPhysHeight - iPhysOffsetY - GetDeviceCaps(hdc, VERTRES));
    if(debug&DEBUGinterface) fprintf(debug_fh, "plotter: HORZRES: %d VERTRES: %d\n", GetDeviceCaps(hdc, HORZRES), GetDeviceCaps(hdc, VERTRES));
    if(debug&DEBUGinterface) fprintf(debug_fh, "plotter: iLeftAdjust: %d iRightAdjust: %d iTopAdjust: %d iBottomAdjust: %d\n",
	iLeftAdjust, iRightAdjust, iTopAdjust, iBottomAdjust);

    dev_xmin = 0;
    dev_xmax = iPhysWidth;
    dev_ymin = 0;
    dev_ymax = iPhysHeight;
    dev_xmargin = iPhysWidth-GetDeviceCaps(hdc, HORZRES);
    dev_ymargin = iPhysHeight-GetDeviceCaps(hdc, VERTRES);

    plotter_coord_init(&m);
    plotter_coord_dump(&m, "Unit matrix");
    
    xscale = ((double) GetDeviceCaps(hdc, LOGPIXELSX))/254.0;
    yscale = ((double) GetDeviceCaps(hdc, LOGPIXELSY))/254.0;
    plotter_coord_scale2(&m, xscale, yscale);
    plotter_coord_dump(&m, "After scale 0.1mm -> device coordinates");
    dev_xmin /= xscale;
    dev_xmax /= xscale;
    dev_ymin /= yscale;
    dev_ymax /= yscale;
    dev_xmargin /= xscale;
    dev_ymargin /= yscale;

    if(debug&DEBUGinterface) fprintf(debug_fh, "before rotate: dev_xmin: %g dev_xmax: %g dev_ymin: %g dev_ymax: %g dev_xmargin: %g dev_ymargin: %g\n", dev_xmin, dev_xmax, dev_ymin, dev_ymax, dev_xmargin, dev_ymargin);
    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, 1.57079632679489661923);	/* 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;
      dtmp1 = dev_xmargin;
      dev_xmargin = dev_ymargin;
      dev_ymargin = dtmp1;
    }
    if(debug&DEBUGinterface) fprintf(debug_fh, "after rotate: dev_xmin: %g dev_xmax: %g dev_ymin: %g dev_ymax: %g dev_xmargin: %g dev_ymargin: %g\n", dev_xmin, dev_xmax, dev_ymin, dev_ymax, dev_xmargin, dev_ymargin);
    if(debug&DEBUGinterface) fprintf(debug_fh, "HORZRES: %d plotter_xmax-plotter_xmin: %d\n", GetDeviceCaps(hdc, HORZRES), plotter_xmax-plotter_xmin); 
    if(debug&DEBUGinterface) fprintf(debug_fh, "VERTRES: %d plotter_ymax-plotter_ymin: %d\n", GetDeviceCaps(hdc, VERTRES), plotter_ymax-plotter_ymin); 
    xscale = (dev_xmax-dev_xmin-dev_xmargin*2.0)/((double)(plotter_xmax-plotter_xmin));
    yscale = (dev_ymax-dev_ymin-dev_ymargin*2.0)/((double)(plotter_ymax-plotter_ymin));
    if(debug&DEBUGinterface) fprintf(debug_fh, "plotter out: xscale: %g yscale: %g\n", xscale, yscale);
    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);
    }
    
    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);
    if(debug&DEBUGinterface) fflush(debug_fh);

    memset(&di, 0, sizeof(di));
    di.cbSize = sizeof(di);
    di.lpszDocName = "GIER Plotter";
    oldmode=SetBkMode(hdc, TRANSPARENT);
    hbr = GetStockObject(NULL_BRUSH);
    hbrold = SelectObject(hdc, hbr);

    /* Correct for margins */
    m.v[0] += -iPhysOffsetX;
    m.v[1] =  iPhysHeight - iPhysOffsetY - m.v[1];
    /* Origin is at the top */
    m.a[1][0] *= -1.0;
    m.a[1][1] *= -1.0;

    if(StartDoc(hdc, &di)>0)
    {
      for(iColCopy=0; iColCopy<((pd.Flags&PD_COLLATE)?pd.nCopies:1); iColCopy++)
      {
	iPage=0; /* Only one page... */
	iPage++;
	if((!(pd.Flags&PD_PAGENUMS)) ||
	   (iPage>=pd.nFromPage && iPage<=pd.nToPage))
	{
	  for(iNonColCopy = 0; iNonColCopy < ((pd.Flags&PD_COLLATE)?1:pd.nCopies); iNonColCopy++)
	  {
	    if(StartPage(hdc)<0)
	    {
	      bSuccess = FALSE;
	      break;
	    }
	    /* SetViewportOrgEx(hdc, iLeftAdjust, iTopAdjust, NULL); */

	    line = plotter_firstline;
	    while(line)
	    {
	      if(line->buffersize>0)
	      {
		x=line->x;
		y=line->y;
		points[0].x = (int) round(plotter_coord_x(&m, (double)x, (double)y));
		points[0].y = (int) round(plotter_coord_y(&m, (double)x, (double)y));
		hpen = CreatePen(PS_SOLID, (int) (unit*((double)line->linewidth)),
		       RGB(line->linecolor==1?255:0,
	                   line->linecolor==2?255:0,
			   line->linecolor==3?255:0));
		hpenold = SelectObject(hdc, hpen);
		for(i=0; i<line->buffersize; i++)
		{
		  x += plotter_dx[line->buffer[i]];
		  y += plotter_dy[line->buffer[i]];
		  points[i+1].x = (int) round(plotter_coord_x(&m, (double)x, (double)y));
		  points[i+1].y = (int) round(plotter_coord_y(&m, (double)x, (double)y));
		}
		Polyline(hdc, points, line->buffersize+1);
		if(debug&DEBUGinterface) fprintf(debug_fh, "plotter out: Plot %d points.\n", line->buffersize);
		if(debug&DEBUGinterface) fflush(debug_fh);
		SelectObject(hdc, hpenold);
		DeleteObject(hpen);
	      }

	      next = line->nextline;
	      line = next;
	    }
	    if(debug&DEBUGinterface) fprintf(debug_fh, "plotter out: End page\n");
	    if(debug&DEBUGinterface) fflush(debug_fh);
	    if(EndPage(hdc)<0)
	    {
	      bSuccess = FALSE;
	      break;
	    }
	  }
	}
	if(!bSuccess || bUserAbort) break;
      }
      EndDoc(hdc);
    }

    SelectObject(hdc, hbrold);
    DeleteObject(hbr);
    SetBkMode(hdc, oldmode);
    DeleteDC(hdc);

    plotter_release_storage();
  }
}

LRESULT CALLBACK plotterWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;
  int i,ilen;
  OPENFILENAME openfile;
  int status;
  char tmp[20];

  PAINTSTRUCT ps;
  switch(message)
  {
  case WM_COMMAND:
    if(debug&DEBUGinterface) fprintf(debug_fh, "plotterWndProc. WM_COMMAND.\n");
    if(lParam == (LPARAM) plotter_linewidth_edit)
    {
      i = plotter_linewidth;
      *((short *)tmp) = sizeof(tmp)-1;
      ilen=SendMessage(plotter_linewidth_edit, EM_GETLINE, 0, (LPARAM) tmp);
      tmp[ilen] = '\0';
      if(debug&DEBUGinterface) fprintf(debug_fh, "Get %d chars from EM_GETLINE: '%s'\n", ilen, tmp);
      if(ilen>0)
      {
	sscanf(tmp, "%d", &plotter_linewidth);
	if(debug&DEBUGinterface) fprintf(debug_fh, "Setting plotter_linewidth to: %d\n", plotter_linewidth);
	if(i != plotter_linewidth) plotter_change_setup();
      }
    }
    else if(lParam == (LPARAM) plotter_linecolor_edit)
    {
      i = plotter_linecolor;
      plotter_linecolor=SendMessage(plotter_linecolor_edit, CB_GETCURSEL, 0, 0);
      if(debug&DEBUGinterface) fprintf(debug_fh, "Setting plotter_linecolor to: %d\n", plotter_linecolor);
      if(i != plotter_linecolor) plotter_change_setup();
    }
    else
    {
      switch(LOWORD(wParam))
      {
      case IDM_PLOTTER_SETUP:
	if(DialogBox(mainhInstance, "PLOTTERWINDOW", mainhwnd, PlotterDlgProc)==IDM_PLOTTER_OK)
	{
	  plotter_update();
	  plotter_update_status();
	}
	break;
      case IDM_PLOTTER_STARTPLOT:
	plotter_plot_storage(hWnd);
	break;
      case IDM_PLOTTER_CANCELPLOT:
	plotter_release_storage();
	break;
      }
    }
    break;
  case WM_PAINT:
    hdc = BeginPaint(hWnd, &ps);
    EndPaint(hWnd, &ps);
    plotter_update();
    break;
  case WM_DESTROY:
    plotterhwnd = NULL;
    update_viewmenu();
    break;
  default:
    return(DefWindowProc(hWnd, message, wParam, lParam));
  }
  return 0;
}

static BOOL plotterInitApplication(HINSTANCE hInstance)
{
  WNDCLASS wc;
  BOOL rc;

  wc.style = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc = plotterWndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = NULL;
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
  wc.lpszMenuName = "PLOTTERMENU";
  wc.lpszClassName = "GIERplotter";

  rc=RegisterClass(&wc);
  return rc;
}

static BOOL plotterInitInstance(HINSTANCE hinstance, INT ncmdshow)
{
  RECT warea;

  warea.left = 0;
  warea.right = font_width*PLOTTER_WINDOW-1;
  warea.top = 0;
  warea.bottom = 1*font_height+100;
  AdjustWindowRect(&warea, WS_OVERLAPPEDWINDOW, True);

  plotterhwnd = CreateWindow("GIERplotter", "GIER plotter", WS_OVERLAPPEDWINDOW,
         325, 325, warea.right-warea.left+1, warea.bottom-warea.top+1,
	 (HWND)NULL,(HMENU)NULL, hinstance, (LPSTR)NULL);

  if(plotterhwnd == NULL) return 0;

  ShowWindow(plotterhwnd, ncmdshow);
  UpdateWindow(plotterhwnd);

  plotter_update();
  plotter_update_status();

  return 1;
}

static plotter_setup1st()
{
  PAGESETUPDLG pdlg;
  LPDEVNAMES lpdevnames;
  /* Set up default plotter */
  memset(&pdlg, 0, sizeof(pdlg));
  pdlg.lStructSize = sizeof(pdlg);
  pdlg.hwndOwner = mainhwnd;
  pdlg.Flags = PSD_RETURNDEFAULT|PSD_MARGINS|PSD_INTHOUSANDTHSOFINCHES;
  pdlg.rtMargin.top = 250;
  pdlg.rtMargin.bottom = 250;
  pdlg.rtMargin.left = 250;
  pdlg.rtMargin.right = 250;
  if(debug&DEBUGinterface) fprintf(debug_fh, "Calling PageSetupDlg to get default printer for plotter.\n");
  PageSetupDlg(&pdlg);
  plotter_lastMargin.top = pdlg.rtMargin.top;
  plotter_lastMargin.bottom = pdlg.rtMargin.bottom;
  plotter_lastMargin.left = pdlg.rtMargin.left;
  plotter_lastMargin.right = pdlg.rtMargin.right;
  plotter_marginunits = pdlg.Flags&(PSD_INTHOUSANDTHSOFINCHES|PSD_INHUNDREDTHSOFMILLIMETERS);
  dump_pdlg(&pdlg);
  plotter_hDevMode = pdlg.hDevMode;
  plotter_hDevNames = pdlg.hDevNames;
  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "Return from PageSetupDlg.\n");
    lpdevnames = (LPDEVNAMES) GlobalLock(plotter_hDevNames);
    fprintf(debug_fh, "Device: '%s'\n", ((char *) lpdevnames)+lpdevnames->wDeviceOffset);
    fprintf(debug_fh, "Driver: '%s'\n", ((char *) lpdevnames)+lpdevnames->wDriverOffset);
    fprintf(debug_fh, "Output: '%s'\n", ((char *) lpdevnames)+lpdevnames->wOutputOffset);
    fflush(debug_fh);
    GlobalUnlock(lpdevnames);
  }
  plotter_has_been_setup=1;
}

static void plotter_select_create()
{
  char tmp[10];
  plotter_linewidth_text = CreateWindow("STATIC", "Pen width, 0.1mm", WS_CHILD|WS_GROUP|WS_VISIBLE,
		   19, 21, 150, 30,
		   plotterhwnd, (HMENU)NULL, mainhInstance, (LPSTR)NULL);
  plotter_linewidth_edit = CreateWindow("EDIT", "", WS_CHILD|WS_GROUP|WS_VISIBLE|ES_AUTOHSCROLL,
		   180, 21, 32, 24,
		   plotterhwnd, (HMENU)NULL, mainhInstance, (LPSTR)NULL);
  sprintf(tmp, "%d", plotter_linewidth);
  SendMessage(plotter_linewidth_edit, WM_SETTEXT, 0, (LPARAM) tmp);
  plotter_linecolor_text = CreateWindow("STATIC", "Color", WS_CHILD|WS_GROUP|WS_VISIBLE,
		   19, 60, 89, 30,
		   plotterhwnd, (HMENU)NULL, mainhInstance, (LPSTR)NULL);
  plotter_linecolor_edit = CreateWindow("COMBOBOX", "", WS_CHILD|WS_GROUP|WS_VISIBLE|CBS_DROPDOWN,
		   150, 60, 100, 70,
		   plotterhwnd, (HMENU)NULL, mainhInstance, (LPSTR)NULL);
  SendMessage(plotter_linecolor_edit, CB_ADDSTRING, 0, (LPARAM) "Black");
  SendMessage(plotter_linecolor_edit, CB_ADDSTRING, 0, (LPARAM) "Red");
  SendMessage(plotter_linecolor_edit, CB_ADDSTRING, 0, (LPARAM) "Green");
  SendMessage(plotter_linecolor_edit, CB_ADDSTRING, 0, (LPARAM) "Blue");
  SendMessage(plotter_linecolor_edit, CB_SETCURSEL, plotter_linecolor, (LPARAM) 0);
}
plotter_init()
{
  int i;
  double x;
  if(!plotter_has_been_setup) plotter_setup1st();
  plotterInitApplication(mainhInstance);
  plotterInitInstance(mainhInstance, SW_SHOWNORMAL);
  plotter_select_create();
  update_viewmenu();
}

plotter_destroy()
{
  if(plotterhwnd != NULL)
  {
    DestroyWindow(plotterhwnd);
    plotterhwnd=NULL;
    update_viewmenu();
  }
}

void plotter_set_pen(int color,int width)
{
  plotter_linecolor = color;
  plotter_linewidth = width;
}

BOOL plotter_is_visible()
{
  return plotterhwnd != NULL;
}

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;
    plotter_update_status();
  }
  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_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)
{
  unsigned char uc=(unsigned char) c;
  /*
  	Store character in buffer
  */
  if(plotterhwnd == 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();
	plotter_update_status();
      }
      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);
}

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;
  for(i=0; i<44; i++)
  {
    kb12_upper_count[i]=0;
    kb12_lower_count[i]=0;
  }
  for(i=0; i<9; i++)
  {
    kb12_aux_count[i]=0;
  }
  for(i=0; i<12; i++)
  {
    nimbi_count[i]=0;
  }
  kb12_count=0;
}

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

  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(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<9; 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)
  {
    *lower = BUS&ONE0_41;
  }
  else
  {
    *lower = get_register();
  }
}


void kb1_update()
{
  double x;
  int i;
  
  HDC hdc;
  HPEN hpen, hpenold;
  HBRUSH hbr, hbrold;

  if(kb1hwnd == NULL) return;
  if(kb12_count==0) kb12_count=1;

  if(debug&DEBUGinterface) {fprintf(debug_fh, "kb1_update called.\n"); fflush(debug_fh);}

  hdc = GetDC(kb1hwnd);
  hbr = GetStockObject(BLACK_BRUSH);
  hbrold = SelectObject(hdc, hbr);

  for(i=0; i<42; i++)
  {
    x = BIT0x + (BIT41x-BIT0x)/41.0*((double)i) + 0.5;
    hpen = CreatePen(PS_SOLID, 2, lampcolors[((unsigned char)(kb12_lower_count[i]*255/kb12_count))]);
    hpenold = SelectObject(hdc, hpen);
    MoveToEx(hdc, (int) x, kb1_low_y1, NULL);
    LineTo(hdc, (int) x, kb1_low_y2);
    SelectObject(hdc, hpenold);
    DeleteObject(hpen);
  }

  for(i=0; i<44; i++)
  {
    x = BITOx + (BITbyx-BITOx)/43.0*((double)i) + 0.5;
    hpen = CreatePen(PS_SOLID, 2, lampcolors[((unsigned char)(kb12_upper_count[i]*255/kb12_count))]);
    hpenold = SelectObject(hdc, hpen);

    MoveToEx(hdc, (int) x, kb1_high_y1, NULL);
    LineTo(hdc, (int) x, kb1_high_y2);
    SelectObject(hdc, hpenold);
    DeleteObject(hpen);
  }
  ReleaseDC(kb1hwnd, hdc);
  SelectObject(hdc, hbrold);
  DeleteObject(hbr);
}

LRESULT CALLBACK kb1WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;
  HDC hdcBitmap;
  HBITMAP hbmp, hbmpOld;
  BITMAP Bitmap_Info;
  LONG bottom, right;
  int changed=0;
  GIERword lower,mask;
  int i;

  PAINTSTRUCT ps;
  switch(message)
  {
  case WM_COMMAND:
    break;
  case WM_PAINT:
    hdc = BeginPaint(hWnd, &ps);
    if(debug&DEBUGinterface)
    {
      fprintf(debug_fh, "kb1 WM_PAINT.\n");
      fprintf(debug_fh, "left: %d, right: %d, top: %d, bottom: %d\n",
             ps.rcPaint.left, ps.rcPaint.right, ps.rcPaint.top, ps.rcPaint.bottom);
    }
    hbmp = LoadBitmap(mainhInstance, "kb1_bitmap");
    GetObject(hbmp, sizeof(Bitmap_Info), &Bitmap_Info);
    hdcBitmap = CreateCompatibleDC(hdc);
    hbmpOld = SelectObject(hdcBitmap, hbmp);
    if(debug&DEBUGinterface) fprintf(debug_fh, "kb1 bitmap. width: %d height: %d\n", Bitmap_Info.bmWidth, Bitmap_Info.bmHeight);
    right = ps.rcPaint.right;
    if(right >= Bitmap_Info.bmWidth) right=Bitmap_Info.bmWidth-1;
    bottom = ps.rcPaint.bottom;
    if(bottom >= Bitmap_Info.bmHeight) bottom=Bitmap_Info.bmHeight-1;
    BitBlt(hdc, ps.rcPaint.left, ps.rcPaint.top, right-ps.rcPaint.left+1, bottom-ps.rcPaint.top+1,
           hdcBitmap, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
    if(debug&DEBUGinterface) fprintf(debug_fh, "bitblt: %d %d %d %d < %d %d\n",
      ps.rcPaint.left, ps.rcPaint.top, right-ps.rcPaint.left+1, bottom-ps.rcPaint.top+1,
      ps.rcPaint.left, ps.rcPaint.top);
    SelectObject(hdcBitmap, hbmpOld);
    DeleteDC(hdcBitmap);
    DeleteObject(hbmp);
    DeleteObject(hbmpOld);
    EndPaint(hWnd, &ps);
    kb1_update();
    break;
  case WM_LBUTTONDOWN:
    lower = get_register();
    if(debug&DEBUGinterface) fprintf(debug_fh, "x: %d y: %d\n", MAKEPOINTS(lParam).x, MAKEPOINTS(lParam).y);
    mask = ONE0;
    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(kb1hwnd, "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();
    }
    break;
  case WM_RBUTTONDOWN:
    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();
    }
    break;
  case WM_DESTROY:
    kb1hwnd = NULL;
    update_viewmenu();
    break;
  default:
    return(DefWindowProc(hWnd, message, wParam, lParam));
  }
  return 0;
}

static BOOL kb1InitApplication(HINSTANCE hInstance)
{
  WNDCLASS wc;
  BOOL rc;

  wc.style = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc = kb1WndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = NULL;
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
  wc.lpszMenuName = NULL;
  wc.lpszClassName = "GIERkb1";

  rc=RegisterClass(&wc);
  return rc;
}

static BOOL kb1InitInstance(HINSTANCE hinstance, INT ncmdshow)
{
  RECT warea;

  warea.left = 0;
  warea.right = kb1_width-1;
  warea.top = 0;
  warea.bottom = kb1_height-1;
  AdjustWindowRect(&warea, WS_OVERLAPPEDWINDOW, False);

  kb1hwnd = CreateWindow("GIERkb1", "GIER Main Control Board", WS_OVERLAPPEDWINDOW,
         555, 80, warea.right-warea.left+1, warea.bottom-warea.top+1,
	 (HWND)NULL,(HMENU)NULL, hinstance, (LPSTR)NULL);

  if(kb1hwnd == NULL) return 0;

  ShowWindow(kb1hwnd, ncmdshow);
  UpdateWindow(kb1hwnd);

  return 1;
}

kb1_init()
{
  int i;
  double x;
  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "kb1_init called\n");
    fflush(debug_fh);
  }
  lampcolor_init();
  kb1InitApplication(mainhInstance);
  kb1InitInstance(mainhInstance, SW_SHOWNORMAL);
  update_viewmenu();
  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;
    }
  }
  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "kb1_init finished\n");
    fflush(debug_fh);
  }
}

kb1_destroy()
{
  if(kb1hwnd != NULL)
  {
    DestroyWindow(kb1hwnd);
    kb1hwnd=NULL;
    update_viewmenu();
  }
}

BOOL kb1_is_visible()
{
  return kb1hwnd != NULL;
}

static update_viewmenu()
{
  HMENU view_menu;

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

  view_menu = GetMenu(mainhwnd);

  CheckMenuItem(view_menu, IDM_MAIN_VIEW_KB1, MF_BYCOMMAND | (kb1_is_visible() ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(view_menu, IDM_MAIN_VIEW_KB2, MF_BYCOMMAND | (kb2_is_visible() ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(view_menu, IDM_MAIN_VIEW_TYPEWRITER, MF_BYCOMMAND | (typewriter_is_visible() ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(view_menu, IDM_MAIN_VIEW_READER, MF_BYCOMMAND | (reader_is_visible() ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(view_menu, IDM_MAIN_VIEW_PUNCH, MF_BYCOMMAND | (punch_is_visible() ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(view_menu, IDM_MAIN_VIEW_PRINTER, MF_BYCOMMAND | (printer_is_visible() ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(view_menu, IDM_MAIN_VIEW_PLOTTER, MF_BYCOMMAND | (plotter_is_visible() ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(view_menu, IDM_MAIN_VIEW_NIMBI, MF_BYCOMMAND | (nimbi_is_visible() ? MF_CHECKED: MF_UNCHECKED));
  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "update_viewmenu finished\n");
    fflush(debug_fh);
  }
}

static update_optionsmenu()
{
  HMENU option_menu;

  option_menu = GetMenu(mainhwnd);

  CheckMenuItem(option_menu, IDM_MAIN_OPTION_CH0, MF_BYCOMMAND | (track0_open ? MF_UNCHECKED: MF_CHECKED));
  CheckMenuItem(option_menu, IDM_MAIN_OPTION_CH131, MF_BYCOMMAND | (track1_31_open ? MF_UNCHECKED: MF_CHECKED));
  CheckMenuItem(option_menu, IDM_MAIN_OPTION_ZERO, MF_BYCOMMAND | (switch_k0 ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(option_menu, IDM_MAIN_OPTION_SOUND, MF_BYCOMMAND | (sound_enabled ? MF_CHECKED: MF_UNCHECKED));
}

static update_testmenu()
{
  HMENU test_menu;

  test_menu = GetMenu(mainhwnd);
  CheckMenuItem(test_menu, IDM_TEST_OPTION_NORMAL, MF_BYCOMMAND | ((testmode==0) ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(test_menu, IDM_TEST_OPTION_WRITE, MF_BYCOMMAND | ((testmode==1) ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(test_menu, IDM_TEST_OPTION_READ, MF_BYCOMMAND | ((testmode==2) ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(test_menu, IDM_TEST_OPTION_RIGHTSHIFT, MF_BYCOMMAND | ((testmode==3) ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(test_menu, IDM_TEST_OPTION_LEFTSHIFT, MF_BYCOMMAND | ((testmode==4) ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(test_menu, IDM_TEST_OPTION_INDICATOR, MF_BYCOMMAND | ((testmode==5) ? MF_CHECKED: MF_UNCHECKED));
}

static update_aarhusmenu()
{
  HMENU aarhus_menu;

  aarhus_menu = GetMenu(mainhwnd);
  CheckMenuItem(aarhus_menu, IDM_AARHUS_OPTION_38, MF_BYCOMMAND | ((aarhus_mode==0) ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(aarhus_menu, IDM_AARHUS_OPTION_1, MF_BYCOMMAND | ((aarhus_mode==1) ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(aarhus_menu, IDM_AARHUS_OPTION_319, MF_BYCOMMAND | ((aarhus_mode==2) ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(aarhus_menu, IDM_AARHUS_OPTION_639, MF_BYCOMMAND | ((aarhus_mode==3) ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(aarhus_menu, IDM_AARHUS_OPTION_959, MF_BYCOMMAND | ((aarhus_mode==4) ? MF_CHECKED: MF_UNCHECKED));
}

static update_debugmenu()
{
  HMENU debug_menu;

  checkdebug();

  debug_menu = GetMenu(mainhwnd);

  CheckMenuItem(debug_menu, IDM_MAIN_DEBUG_MEMORY, MF_BYCOMMAND | ((debug&DEBUGmemory) ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(debug_menu, IDM_MAIN_DEBUG_MICROSTEP, MF_BYCOMMAND | ((debug&DEBUGmicrostep) ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(debug_menu, IDM_MAIN_DEBUG_MASKINTAL, MF_BYCOMMAND | ((debug&DEBUGmaskintal) ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(debug_menu, IDM_MAIN_DEBUG_FLYDENDE, MF_BYCOMMAND | ((debug&DEBUGflydende) ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(debug_menu, IDM_MAIN_DEBUG_BUS, MF_BYCOMMAND | ((debug&DEBUGBUS) ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(debug_menu, IDM_MAIN_DEBUG_REGISTERS, MF_BYCOMMAND | ((debug&DEBUGregisters) ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(debug_menu, IDM_MAIN_DEBUG_INDICATOR, MF_BYCOMMAND | ((debug&DEBUGindicator) ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(debug_menu, IDM_MAIN_DEBUG_INTERFACE, MF_BYCOMMAND | ((debug&DEBUGinterface) ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(debug_menu, IDM_MAIN_DEBUG_DRUM, MF_BYCOMMAND | ((debug&DEBUGdrum) ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(debug_menu, IDM_MAIN_DEBUG_SOUND, MF_BYCOMMAND | ((debug&DEBUGsound) ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(debug_menu, IDM_MAIN_DEBUG_BUFFER, MF_BYCOMMAND | ((debug&DEBUGbuffer) ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(debug_menu, IDM_MAIN_DEBUG_EXECUTE, MF_BYCOMMAND | ((debug&DEBUGexecute) ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(debug_menu, IDM_MAIN_DEBUG_TEXT, MF_BYCOMMAND | ((debug&DEBUGtext) ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(debug_menu, IDM_MAIN_DEBUG_STAT, MF_BYCOMMAND | ((debug&DEBUGstat) ? MF_CHECKED: MF_UNCHECKED));
  CheckMenuItem(debug_menu, IDM_MAIN_DEBUG_DRUMRACE, MF_BYCOMMAND | ((debug&DEBUGdrumrace) ? MF_CHECKED: MF_UNCHECKED));
}

static void set_bdisk(int bdisk, int type)
{
  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);
  }
}

BOOL CALLBACK NewDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  char tmp_label[80];
  int i;
  switch(message)
  {
  case WM_INITDIALOG:
    newdrum=30;
    newbuffer=1;
    set_check(GetDlgItem(hWnd, IDM_NEW_DISK), TRUE);
    set_check(GetDlgItem(hWnd, IDM_NEW_BUFFER), TRUE);
    /* This only works for MAXBDISKS equal to 2 */
    for(i=0; bdisktypes[i].name!=NULL; i++)
    {
      if(bdisktypes[i].size==0)
      {
	sprintf(tmp_label, "%s", bdisktypes[i].name);
      }
      else
      {
	sprintf(tmp_label, "%s: %d tracks of %d cells", bdisktypes[i].name, bdisktypes[i].size, bdisktypes[i].tracksize);
      }
      SendMessage(GetDlgItem(hWnd, IDM_NEW_BDISK8), CB_ADDSTRING, 0, (LPARAM) tmp_label);
      SendMessage(GetDlgItem(hWnd, IDM_NEW_BDISK9), CB_ADDSTRING, 0, (LPARAM) tmp_label);
    }
    newBDisk_present[0] = 0;
    SendMessage(GetDlgItem(hWnd, IDM_NEW_BDISK8), CB_SETCURSEL, 0, (LPARAM) 0);
    newBDisk_present[1] = 0;
    SendMessage(GetDlgItem(hWnd, IDM_NEW_BDISK9), CB_SETCURSEL, 0, (LPARAM) 0);
    break;
  case WM_COMMAND:
    switch(LOWORD(wParam))
    {
    case IDM_NEW_OK:
      EndDialog(hWnd, IDM_NEW_OK);
      return TRUE;
    case IDM_NEW_CANCEL:
      EndDialog(hWnd, IDM_NEW_CANCEL);
      return TRUE;
    case IDM_NEW_DRUM1:
      newdrum=1;
      if(debug&DEBUGinterface) fprintf(debug_fh, "newdrum: %d newbuffer: %d\n", newdrum, newbuffer);
      break;
    case IDM_NEW_DRUM2:
      newdrum=2;
      if(debug&DEBUGinterface) fprintf(debug_fh, "newdrum: %d newbuffer: %d\n", newdrum, newbuffer);
      break;
    case IDM_NEW_DRUM3:
      newdrum=3;
      if(debug&DEBUGinterface) fprintf(debug_fh, "newdrum: %d newbuffer: %d\n", newdrum, newbuffer);
      break;
    case IDM_NEW_DISK:
      newdrum=30;
      if(debug&DEBUGinterface) fprintf(debug_fh, "newdrum: %d newbuffer: %d\n", newdrum, newbuffer);
      break;
    case IDM_NEW_DISK2:
      newdrum=30*2;
      if(debug&DEBUGinterface) fprintf(debug_fh, "newdrum: %d newbuffer: %d\n", newdrum, newbuffer);
      break;
    case IDM_NEW_DISK3:
      newdrum=30*3;
      if(debug&DEBUGinterface) fprintf(debug_fh, "newdrum: %d newbuffer: %d\n", newdrum, newbuffer);
      break;
    case IDM_NEW_DISK4:
      newdrum=30*4;
      if(debug&DEBUGinterface) fprintf(debug_fh, "newdrum: %d newbuffer: %d\n", newdrum, newbuffer);
      break;
    case IDM_NEW_DISK5:
      newdrum=30*5;
      if(debug&DEBUGinterface) fprintf(debug_fh, "newdrum: %d newbuffer: %d\n", newdrum, newbuffer);
      break;
    case IDM_NEW_BUFFER:
      newbuffer=get_check(GetDlgItem(hWnd, IDM_NEW_BUFFER));
      if(debug&DEBUGinterface) fprintf(debug_fh, "newdrum: %d newbuffer: %d\n", newdrum, newbuffer);
      break;
    case IDM_NEW_BDISK8:
      i=SendMessage(GetDlgItem(hWnd, IDM_NEW_BDISK8), CB_GETCURSEL, 0, 0);
      if(debug&DEBUGinterface) fprintf(debug_fh, "IDM_NEW_BDISK8: %d\n", i);
      set_bdisk(0, i);
      break;
    case IDM_NEW_BDISK9:
      i=SendMessage(GetDlgItem(hWnd, IDM_NEW_BDISK9), CB_GETCURSEL, 0, 0);
      if(debug&DEBUGinterface) fprintf(debug_fh, "IDM_NEW_BDISK9: %d\n", i);
      set_bdisk(1, i);
      break;
    default:
      return TRUE;
    }
  default:
    return FALSE;
  }
  return FALSE;
}

LRESULT CALLBACK mainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;
  SIZE tsize;
  PAINTSTRUCT ps;
  OPENFILENAME openfile;
  char filename[200];
  int status;

  switch(message)
  {
  case WM_PAINT:
    hdc = BeginPaint(hWnd, &ps);
    if(debug&DEBUGinterface)
    {
      fprintf(debug_fh, "main WM_PAINT.\n");
      fprintf(debug_fh, "left: %d, right: %d, top: %d, bottom: %d\n",
             ps.rcPaint.left, ps.rcPaint.right, ps.rcPaint.top, ps.rcPaint.bottom);
    }

    font = CreateFont(default_textsize,0,0,0,400,0,0,0,ANSI_CHARSET,OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,
		      DEFAULT_QUALITY,FIXED_PITCH|FF_MODERN,"Courier New");
    small_font = CreateFont(small_textsize,0,0,0,400,0,0,0,ANSI_CHARSET,OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,
			DEFAULT_QUALITY,FIXED_PITCH|FF_MODERN,"Courier New");
    symbol_font = CreateFont(default_textsize,0,0,0,400,0,0,0,SYMBOL_CHARSET,OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,
			DEFAULT_QUALITY,FIXED_PITCH|FF_MODERN,"Symbol");
    demo_font = CreateFont(demo_textsize,0,0,0,400,0,0,0,ANSI_CHARSET,OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,
		      DEFAULT_QUALITY,FIXED_PITCH|FF_MODERN,"Courier New");
    oldfont=SelectObject(hdc, font);
    GetTextExtentPoint32(hdc, "W", 1, &tsize);
    SelectObject(hdc, oldfont);
    font_width = tsize.cx;
    font_height = tsize.cy;
    if(debug&DEBUGinterface) fprintf(debug_fh, "font width: %d height: %d\n", font_width, font_height);
    oldfont=SelectObject(hdc, demo_font);
    GetTextExtentPoint32(hdc, "W", 1, &tsize);
    SelectObject(hdc, oldfont);
    demo_font_width = tsize.cx;
    demo_font_height = tsize.cy;
    if(debug&DEBUGinterface) fprintf(debug_fh, "demo font width: %d height: %d\n", demo_font_width, demo_font_height);
    EndPaint(hWnd, &ps);
    break;
  case WM_COMMAND:
    switch(LOWORD(wParam))
    {
    case IDM_MAIN_FILE_NEW:
      if(DialogBox(mainhInstance, "NEWDIALOG", mainhwnd, NewDlgProc)==IDM_NEW_OK)
      {
	if(confirm(mainhwnd, "OK to erase everything and load empty core and backing store?"))
	{
	  GIER_init(newdrum,newbuffer,newBDisk_tracksize,newBDisk_present,newBDisk_size);
	}
      }
      break;
    case IDM_MAIN_FILE_OPEN:
      filename[0]='\0';
      openfile.lStructSize = sizeof(openfile);
      openfile.hwndOwner = mainhwnd;
      openfile.lpstrFilter = "GIER files (*.gier)\0*.gier\0All files (*.*)\0*.*\0\0";
      openfile.lpstrCustomFilter = NULL;
      openfile.nMaxCustFilter = 0;
      openfile.nFilterIndex = 0;
      openfile.lpstrFile = filename;
      openfile.nMaxFile = sizeof(filename)-1;
      openfile.lpstrFileTitle = NULL;
      openfile.nMaxFileTitle = 0;
      openfile.lpstrInitialDir = NULL;
      openfile.lpstrTitle = NULL;
      openfile.Flags = 0;
      openfile.nFileOffset = 0;
      openfile.nFileExtension = 0;
      openfile.lpstrDefExt = "gier";
      openfile.lCustData = 0;
      openfile.lpfnHook = NULL;
      openfile.lpTemplateName = NULL;
      status=GetOpenFileName(&openfile);
      if(status != 0)
      {
	if(!strncmp(filename+openfile.nFileExtension, "gie", 3))
	{
          filename[openfile.nFileExtension+3] = 'r';
          filename[openfile.nFileExtension+4] = '\0';
	}
        if(debug&DEBUGinterface) fprintf(debug_fh, "Opening GIER file: %s\n", filename);
	if(confirm(mainhwnd, "OK to erase everything and load new core and backing store?"))
	{
	  if(GIER_load(filename)==0)
	  {
            MessageBox(mainhwnd, "Reading the file failed.\nDefault setup has been loaded.",
	               "Read failed", MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL);
	    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();
	}
      }
      break;
    case IDM_MAIN_FILE_SAVE:
      if(last_filename != NULL)
      {
        GIER_save(last_filename);
	break;
      }
      /* else bump into save as: */
    case IDM_MAIN_FILE_SAVEAS:
      filename[0]='\0';
      if(last_filename != NULL) strcpy(filename, last_filename);
      openfile.lStructSize = sizeof(openfile);
      openfile.hwndOwner = mainhwnd;
      openfile.lpstrFilter = "GIER files (*.gier)\0*.gier\0All files (*.*)\0*.*\0\0";
      openfile.lpstrCustomFilter = NULL;
      openfile.nMaxCustFilter = 0;
      openfile.nFilterIndex = 0;
      openfile.lpstrFile = filename;
      openfile.nMaxFile = sizeof(filename)-1;
      openfile.lpstrFileTitle = NULL;
      openfile.nMaxFileTitle = 0;
      openfile.lpstrInitialDir = NULL;
      openfile.lpstrTitle = NULL;
      openfile.Flags = 0;
      openfile.nFileOffset = 0;
      openfile.nFileExtension = 0;
      openfile.lpstrDefExt = "gier";
      openfile.lCustData = 0;
      openfile.lpfnHook = NULL;
      openfile.lpTemplateName = NULL;
      status=GetSaveFileName(&openfile);
      if(status != 0)
      {
	if(!strncmp(filename+openfile.nFileExtension, "gie", 3))
	{
          filename[openfile.nFileExtension+3] = 'r';
          filename[openfile.nFileExtension+4] = '\0';
	}
        if(last_filename != NULL) free(last_filename);
	last_filename = malloc(strlen(filename)+1);
	strcpy(last_filename, filename);

	GIER_save(filename);
      }
      break;
    case IDM_MAIN_FILE_QUIT:
      /* DestroyWindow(hWnd); */
      if(debug&DEBUGinterface)
      {
	fprintf(debug_fh, "quit.\n");
	fflush(debug_fh);
      }
      if(confirm(mainhwnd, "OK to exit GIER simulator?")) GIER_exit();
      break;
    case IDM_MAIN_VIEW_KB1:
      if(kb1hwnd == 0)
      {
	kb1_init();
      }
      else
      {
	kb1_destroy();
      }
      break;
    case IDM_MAIN_VIEW_KB2:
      if(kb2hwnd == 0)
      {
	kb2_init();
      }
      else
      {
	kb2_destroy();
      }
      break;
    case IDM_MAIN_VIEW_TYPEWRITER:
      if(typewriterhwnd == 0)
      {
	typewriter_init();
      }
      else
      {
	typewriter_destroy();
      }
      break;
    case IDM_MAIN_VIEW_READER:
      if(readerhwnd == 0)
      {
	reader_init();
      }
      else
      {
	reader_destroy();
      }
      break;
    case IDM_MAIN_VIEW_PUNCH:
      if(punchhwnd == 0)
      {
	punch_init();
      }
      else
      {
	punch_destroy();
      }
      break;
    case IDM_MAIN_VIEW_PRINTER:
      if(printerhwnd == 0)
      {
	printer_init();
      }
      else
      {
	printer_destroy();
      }
      break;
    case IDM_MAIN_VIEW_PLOTTER:
      if(plotterhwnd == 0)
      {
	plotter_init();
      }
      else
      {
	plotter_destroy();
      }
      break;
    case IDM_MAIN_VIEW_NIMBI:
      if(nimbihwnd == 0)
      {
	nimbi_init();
      }
      else
      {
	nimbi_destroy();
      }
      break;
    case IDM_MAIN_OPTION_CH0:
      if(debug&DEBUGinterface) fprintf(debug_fh, "option_ch0 called.\n");
      track0_open = !track0_open;
      if(debug&DEBUGinterface) fprintf(debug_fh, "track0_open: %d\n", track0_open);
      update_optionsmenu();
      break;
    case IDM_MAIN_OPTION_CH131:
      track1_31_open = !track1_31_open;
      update_optionsmenu();
      break;
    case IDM_MAIN_OPTION_ZERO:
      switch_k0 = !switch_k0;
      update_optionsmenu();
      break;
    case IDM_MAIN_OPTION_SOUND:
      sound_enabled = !sound_enabled;
      update_optionsmenu();
      break;
    case IDM_AARHUS_OPTION_38:
      aarhus_mode = 0;
      update_aarhusmenu();
      break;
    case IDM_AARHUS_OPTION_1:
      aarhus_mode = 1;
      update_aarhusmenu();
      break;
    case IDM_AARHUS_OPTION_319:
      aarhus_mode = 2;
      update_aarhusmenu();
      break;
    case IDM_AARHUS_OPTION_639:
      aarhus_mode = 3;
      update_aarhusmenu();
      break;
    case IDM_AARHUS_OPTION_959:
      aarhus_mode = 4;
      update_aarhusmenu();
      break;
    case IDM_TEST_OPTION_NORMAL:
      testmode = 0;
      update_testmenu();
      break;
    case IDM_TEST_OPTION_WRITE:
      testmode = 1;
      update_testmenu();
      break;
    case IDM_TEST_OPTION_READ:
      testmode = 2;
      update_testmenu();
      break;
    case IDM_TEST_OPTION_RIGHTSHIFT:
      testmode = 3;
      update_testmenu();
      break;
    case IDM_TEST_OPTION_LEFTSHIFT:
      testmode = 4;
      update_testmenu();
      break;
    case IDM_TEST_OPTION_INDICATOR:
      testmode = 5;
      update_testmenu();
      break;
    case IDM_MAIN_DEBUG_NONE:
      debug = 0;
      update_debugmenu();
      break;
    case IDM_MAIN_DEBUG_MEMORY:
      debug ^= DEBUGmemory;
      update_debugmenu();
      break;
    case IDM_MAIN_DEBUG_MICROSTEP:
      debug ^= DEBUGmicrostep;
      update_debugmenu();
      break;
    case IDM_MAIN_DEBUG_MASKINTAL:
      debug ^= DEBUGmaskintal;
      update_debugmenu();
      break;
    case IDM_MAIN_DEBUG_FLYDENDE:
      debug ^= DEBUGflydende;
      update_debugmenu();
      break;
    case IDM_MAIN_DEBUG_BUS:
      debug ^= DEBUGBUS;
      update_debugmenu();
      break;
    case IDM_MAIN_DEBUG_REGISTERS:
      debug ^= DEBUGregisters;
      update_debugmenu();
      break;
    case IDM_MAIN_DEBUG_INDICATOR:
      debug ^= DEBUGindicator;
      update_debugmenu();
      break;
    case IDM_MAIN_DEBUG_INTERFACE:
      debug ^= DEBUGinterface;
      update_debugmenu();
      break;
    case IDM_MAIN_DEBUG_DRUM:
      debug ^= DEBUGdrum;
      update_debugmenu();
      break;
    case IDM_MAIN_DEBUG_SOUND:
      debug ^= DEBUGsound;
      update_debugmenu();
      break;
    case IDM_MAIN_DEBUG_BUFFER:
      debug ^= DEBUGbuffer;
      update_debugmenu();
      break;
    case IDM_MAIN_DEBUG_EXECUTE:
      debug ^= DEBUGexecute;
      update_debugmenu();
      break;
    case IDM_MAIN_DEBUG_TEXT:
      debug ^= DEBUGtext;
      update_debugmenu();
      break;
    case IDM_MAIN_DEBUG_STAT:
      print_statistics();
      debug ^= DEBUGstat;
      update_debugmenu();
      break;
    case IDM_MAIN_DEBUG_DRUMRACE:
      debug ^= DEBUGdrumrace;
      update_debugmenu();
      break;
    case IDM_MAIN_DEBUG_ALL:
      debug = ~0;
      update_debugmenu();
      break;
    }
    break;
  case WM_DESTROY:
    DeleteObject(font);
    DeleteObject(symbol_font);
    DeleteObject(small_font);
    ExitProcess(0);
    break;
  default:
    return(DefWindowProc(hWnd, message, wParam, lParam));
  }
  return 0;
}

static BOOL mainInitApplication(HINSTANCE hInstance)
{
  WNDCLASS wc;
  BOOL rc;

  wc.style = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc = mainWndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = NULL;
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
  wc.lpszMenuName = "MAINMENU";
  wc.lpszClassName = "GIERmain";

  rc=RegisterClass(&wc);
  return rc;
}

static BOOL mainInitInstance(HINSTANCE hinstance, INT ncmdshow)
{
  RECT warea;

  InitCommonControls();

  warea.left = 0;
  warea.right = 230;
  warea.top = 0;
  warea.bottom = 0; /* 0 */
  AdjustWindowRect(&warea, WS_OVERLAPPEDWINDOW, TRUE);

  mainhwnd = CreateWindow("GIERmain", "GIER Main Window", WS_OVERLAPPEDWINDOW,
         25, 15, warea.right-warea.left+1, warea.bottom-warea.top, /* subtract one from height */
	 (HWND)NULL,(HMENU)NULL, hinstance, (LPSTR)NULL);

  if(mainhwnd == NULL) return 0;

  ShowWindow(mainhwnd, ncmdshow);
  UpdateWindow(mainhwnd);

  return 1;
}

static void InitDebug()
{
  if(!debug_active)
  {
    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 GIER_exit()
{
  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "GIER_exit - before GIER_save:.\n");
    fflush(debug_fh);
  }
  GIER_save("default.gier");
  if(debug&DEBUGinterface)
  {
    fprintf(debug_fh, "GIER_exit - before EndDebug:.\n");
    fflush(debug_fh);
  }
  /* dumpgier(); */
  EndDebug();
  if(sound_enabled) w32_sound_close();
  ExitProcess(0);
}

void interface_update()
{
  if(mikrotempi_stop_pressed)
  {
    mikrotempi_stop_pressed=0;
    GIER_stop();
    Mode=Mode1;
    MA=1;
    h=0;
    typewriter_lowercase();
    if(typewriter_clipboard_input!=NULL)
    {
      if(debug&DEBUGinterface) fprintf(debug_fh, "RESET: Stop paste.\n");
      free(typewriter_clipboard_input);
      typewriter_clipboard_input=NULL;
    }
    BY = BY & ~512;
    drum_running=0;
    disk_status=0;
    TO_error=0;
    TR_error=0;
    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();
    }
  }
}

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

void CALLBACK demotimeout(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
  if(debug&DEBUGinterface) fprintf(debug_fh, "demotimeout called.\n");
  KillTimer(demohwnd, idEvent);
  demomode=1;
  GIERdemocommand();
}

LRESULT CALLBACK demoWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;
  int i;
  int status;
  int row1,row2,col1,col2;
  char ch1;
  unsigned char ch2;

  PAINTSTRUCT ps;
  switch(message)
  {
  case WM_COMMAND:
    if(debug&DEBUGinterface) fprintf(debug_fh, "demoWndProc. WM_COMMAND.\n");
    break;
  case WM_PAINT:
    hdc = BeginPaint(hWnd, &ps);
    EndPaint(hWnd, &ps);
    hdc = GetDC(demohwnd);
    oldfont=SelectObject(hdc, demo_font);
    SetTextColor(hdc, RGB(0,0,0));
    SetBkColor(hdc, RGB(255,255,255));
    TextOut(hdc, 10, demo_font_height/2, demotext, sizeof(demotext));
    SelectObject(hdc, oldfont);
    ReleaseDC(demohwnd, hdc);
    break;
  case WM_DESTROY:
    demohwnd = NULL;
    demomode = 0;
    break;
  default:
    return(DefWindowProc(hWnd, message, wParam, lParam));
  }
  return 0;
}
static BOOL demoInitApplication(HINSTANCE hInstance)
{
  WNDCLASS wc;
  BOOL rc;

  wc.style = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc = demoWndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = NULL;
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
  wc.lpszMenuName = NULL;
  wc.lpszClassName = "GIERdemo";

  rc=RegisterClass(&wc);
  return rc;
}

static BOOL demoInitInstance(HINSTANCE hinstance, INT ncmdshow)
{
  RECT warea;
  int i;

  for(i=0;i<sizeof(demotext);i++) demotext[i] = ' ';
  
  warea.left = 0;
  warea.right = 1023;
  warea.top = 0;
  warea.bottom = demo_font_height*1-1;
  AdjustWindowRect(&warea, WS_OVERLAPPEDWINDOW, False);

  /*
  demohwnd = CreateWindow("GIERdemo", "GIER demo", WS_CHILD|WS_BORDER,
         0, 0, warea.right-warea.left+1, warea.bottom-warea.top+1,
	 (HWND)NULL,(HMENU)NULL, hinstance, (LPSTR)NULL);
  */

  demohwnd = CreateWindowEx(WS_EX_TOPMOST|WS_EX_TOOLWINDOW, "GIERdemo", "GIER demo", WS_POPUP,
         0, 0, warea.right-warea.left+1, warea.bottom-warea.top+1,
	 (HWND)NULL,(HMENU)NULL, hinstance, (LPSTR)NULL);

  if(demohwnd == NULL) return 0;

  ShowWindow(demohwnd, ncmdshow);
  UpdateWindow(demohwnd);

  return 1;
}

void GIERdemocommand()
{
  char *bp,*cp;
  int i;
  HDC hdc;

readagain:
  BringWindowToTop(demohwnd);
  bp = fgets(demoline, sizeof(demoline)-1, demofile);
  if(bp == NULL)
  {
    fclose(demofile);
    demofile = NULL;
    demomode = 0;
    /* destroy demo window */
    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);
    SetTimer(demohwnd, 1, demosleep*1000, (TIMERPROC) demotimeout);
    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"))
  {
    cp=strtok(0, "\n");
    if(cp)
    {
      for(i=0;i<sizeof(demotext);i++)
      {
	if(i>=strlen(cp))
	  demotext[i] = ' ';
	else
	  demotext[i] = cp[i];
      }
    }
    else
    {
      for(i=0;i<sizeof(demotext);i++) demotext[i] = ' ';
    }

    hdc = GetDC(demohwnd);
    oldfont=SelectObject(hdc, demo_font);
    SetTextColor(hdc, RGB(0,0,0));
    SetBkColor(hdc, RGB(255,255,255));
    TextOut(hdc, 10, demo_font_height/2, demotext, sizeof(demotext));
    SelectObject(hdc, oldfont);
    ReleaseDC(demohwnd, hdc);
    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, "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);
    reader_current_fd = open(strtok(0,"\n"), O_RDONLY|O_BINARY);
    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");
    strcpy(punch_filename, cp);
    if(punch_current_fd != -1) close(punch_current_fd);
    if(debug&DEBUGinterface) fprintf(debug_fh, "Opening file: %s\n", punch_filename);
    if(!strncmp(punch_filename+strlen(punch_filename)-3, "flx", 3))
    {
      punch_current_fd = open(punch_filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
      punch_filetype = 0;
    }
    else
    {
      punch_current_fd = open(punch_filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
      punch_filetype = 1;
      punch_uppercase = 0;
    }
    if(punch_current_fd == -1) strcpy(punch_filename, "- no tape -");
    punch_update();
    goto readagain;
  }
  else if(!strcasecmp(cp, "FINISHPUNCH"))
  {
  }
  else if(!strcasecmp(cp, "FINISHDEMO"))
  {
    fclose(demofile);
    demofile = NULL;
    demomode = 0;
    /* destroy demo window */
  }
  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)
{
  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;
    /* create window */
    demoInitApplication(mainhInstance);
    demoInitInstance(mainhInstance, SW_SHOWNORMAL);
    GIERdemocommand();
  }
}
int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPreInst, LPSTR lpszCmdLine,int nCmdShow)
{
  int i,j,k;
  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.5;
  fprintf(stderr, "GIER_CLOCK: %d\n", gier_clock);
  fprintf(stderr, "DRUM_CLOCK: %lg\n", drum_clock);
  typewriter_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;
  }
  init_microcode();

  mainhInstance = hInst;

  mainInitApplication(mainhInstance);
  mainInitInstance(mainhInstance, nCmdShow);

  sound_enabled = w32_sound_init();


  if(!sound_enabled)
  {
    EnableMenuItem(GetMenu(mainhwnd), IDM_MAIN_OPTION_SOUND, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
  }

  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;
  if(strlen(lpszCmdLine)>0) GIERdemo(lpszCmdLine);
  GIERmain();
}

interface_run()
{
  MSG Msg;
  BOOL status;

  if(debug&DEBUGinterface) fprintf(debug_fh, "interface_run start.\n");

  /*
  	If GIER is running, peek a message and dispatch,
	and run some microsteps.

	If GIER is halted, just wait for messages
  */

AGAIN:
  
  status=PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE);

AGAIN2:

  if(status)
  {
    TranslateMessage(&Msg);
    DispatchMessage(&Msg);
    goto AGAIN;
  }
  if(running && ((!YE_wait)||is_drum_running()))
  {
    GIER_run();
    kb1_update();
    kb2_update();
    nimbi_update();
    kb12_reset();
    reader_update();
    goto AGAIN;
  }
  kb12_reset();
  kb12_update();
  kb1_update();
  kb2_update();
  nimbi_update();
  kb12_reset();
  status=GetMessage(&Msg, NULL, 0, 0);
  if(status == -1)
  {
FOREVER:
    if(debug&DEBUGinterface) fprintf(debug_fh, "-1 from GetMessage!\n");
    goto FOREVER;
  }
  if(status) goto AGAIN2;

  if(debug&DEBUGinterface) fprintf(debug_fh, "interface_run stop.\n");

  return Msg.wParam;
}
