Source file: /~heha/hs/graphwin.zip/graphwin.c

#pragma option -d -WS -k-
/* -d	doppelte Strings zusammenfassen
   -WS	Windows-Applikation, "intelligente" Rückrufe (via SS==DS)
   -k-	kein Standard-Stapelrahmen
 */
#include <windows.h>
#include <string.h>	//memset
#include <stdlib.h>	//exit
#include <dos.h>	//_argc, _argv
#include "graphwin.h"	//anstelle <graphics.h>
/* Wrapper für die GRAPHICS.LIB ... */

/* zurzeit: fest für VGA-Auflösung, alle 3 Modi */

/* Interne Arbeisweise, Schlüssel-Funktionen:
 > Eine Bitmap verkörpert die Zeichenfläche, notwendig zur Restaurierung
   teilweise verdeckter Flächen (Notwendigkeit bei allen Fenster-Systemen)
 > Im DirectDraw-Modus wird gleichzeitig in das Fenster gezeichnet;
   ansonsten wird die Bitmap bei "Arbeitspausen" hineinkopiert
 > Die Funktion gdiproc() ist eine universelle Hülle für solche Doppelt-
   und (später?) Dreifachzeichnungen
 > Eine Reihe GDI-Objekte (Stift, Schrift, Hintergrund) wird stets parat
   gehalten und per ChangeGdiObject() in allen Kontexten gewechselt
 > Bei Moduswechseln wird die Fenstergröße an die dargestellte Pixelzahl
   angepasst, und zwar bei WM_GetMinMaxInfo(!) 

   Bekannte Unzulänglichkeiten und Fehler:
 - Fehlende Emulation der Palette (Win95 DibDraw erforderlich??) 
 - Inkorrekte Emulation der Hintergrundfarbe (wegen Palette im Original)
 - Vorgabe des Fensters auf VGA-VGAHI (besser: Textmodus)
 - Dürftige Emulation der CONIO (war nicht ursprünglicher Gegenstand)
 - Fenstergrößen-Einstellung noch ohne Zoom und/oder Rollbalken
 - Windows-Füllmuster unterscheiden sich von denen der GRAPHICS.LIB
 - Emulation großer Aspektverhältnisse (z.B. VGALO) ist ebenso verzerrt
 - Keine Windows-Fonts verfügbar, die wie die .CHR-Schriften aussehen
 - Kein Signalhandler-Aufruf beim beabsichtigten Schließen des Fensters
 - Projekt sollte mit "Warnung bei doppelten Symbolen AUS" übersetzt werden!

   Bereits implementierte "Features":
 + Hauptprogramm startet mit main(), nicht mit WinMain()
 + Verwendung des "originalen" DOS-Zeichensatzes für identisches Aussehen
 + Einige Textmodus-typische Funktionen, insbesondere clrscr()
 + Caret- (=Schreibmarken-) Unterstützung - hat EASYWIN auch
 + Unterstützung von Cursor- und Funktionstasten (hat EASYWIN NICHT!)
 + Saubere OEM-Zeichensatz-Unterstützung, auch bei den Tastencodes
 + Sinus-Tabelle (sollte künftig auch extern zugreifbar sein)
 + Direktes Zeichnen sowohl ins Fenster als auch in Hintergrund-Bitmap,
   ermöglicht Beobachtung des Bildaufbaus (="DirectDraw")
 + Indirektes Zeichnen mit Aktualisierung (nur) bei Tastaturabfragen und
   yield() ist schneller, bisweilen schneller als DOS
 + Ganzzahlige Vergrößerung ist vorgesehen
 + Warnung beim "Killen" des Programms, Meldungstext auf deutsch umstellbar
 + Automatisches Schließen des Fensters bei Programmende, wenn nichts
   auf dem Bildschirm steht

   Evtl. vorgesehen:
 * "Dritter Mitschnitt" in Speicher-Metadatei ermöglicht das Neuzeichnen bei
   wechselnder Hintergrundfarbe und Zwischenablage-Export im Vektorformat
 * Durchgängige CONIO-Unterstützung
 * Unterstützung von "bunten" ANSI-Sequenzen

   haftmann#software, 08/02
   Bugfix 12/02 bei initgraph() sowie CrtWindow in .H-Datei
 */

/* Alle Variablen initialisiert Windows mit 0 */
BOOL	CanClose;

//HANDLE	ghInstance;
HWND	CrtWindow;
static HDC	ghScreenDC,ghActiveDC,ghVisualDC;
static HBITMAP ghNullBitmap;	// für DC-Freigabe erforderlich
static HBITMAP	pages[4];	// mehr als 4 Seiten hat kein Modus
/* Die Bitmaps haben die Auflösung des Windows-Bildschirms,
   für maximale Geschwindigkeit von BitBlt() */
static int activepage,visualpage;
int scale=1;		// für vergrößerte Darstellung, später POINT
static int width,height,numpages,colors;
static struct textsettingstype	gTextSettings;
static struct usercharsizetype {
   int multx,divx,multy,divy; }	gUserCharSize={1,1,1,1};
static struct viewporttype	gViewport;	// für Viewport-bezogene Funktionen
static struct linesettingstype	gLineSettings;
static struct arccoordstype	gArcCoords;
static struct palettetype	gDefaultPalette,gPalette;
static struct fillsettingstype	gFillSettings;
static int gGraphResult;
static int gGraphDriver;	// 0=Textmodus, 1..9,10=CGA..VGA,PC3270
static int gGraphMode;		// je nach GraphDriver
static int gInGraphMode;	// entscheidet über Cursor-Darstellung
static int gColor,gBkColor;	// Farb-Indizes (0..15)
static POINT gAspect;		// Seitenverhältnis (setzbar!)
static char gDriverName[16]="GRAPHWIN";
static BYTE gFillPattern[13][8]={		// funktionieren noch nicht!
 {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},	// EMPTY_FILL
 {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},	// SOLID_FILL
 {0x00,0x00,0xFF,0x00,0x00,0x00,0xFF,0x00},	// LINE_FILL		---
 {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80},	// LTSLASH_FILL		///
 {0x03,0x06,0x0C,0x18,0x30,0x60,0xC0,0x81},	// SLASH_FILL		///
 {0x81,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03},	// BKSLASH_FILL		\\\ .
 {0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01},	// LTBKSLASH_FILL	\\\ .
 {0x11,0x22,0x44,0x88,0x11,0x22,0x44,0x88},	// HATCH_FILL		///
 {0x11,0xAA,0x44,0xAA,0x11,0xAA,0x44,0xAA},	// XHATCH_FILL		XXX
 {0x01,0x02,0x04,0x08,0x80,0x40,0x20,0x10},	// INTERLEAVE_FILL	\/\ .
 {0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00},	// WIDE_DOT_FILL
 {0x00,0x66,0x66,0x00,0x00,0x66,0x66,0x00},	// CLOSE_DOT_FILL
 {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}};	// USER_FILL
static HPEN	ghPen,ghoPen;		// Linienfarbe (Standard:Weiß)
static HBRUSH	ghBrush,ghoBrush;	// Füllfarbe (Standard: Schwarz?}
static HFONT	ghFont,ghoFont;		// Schrift ("Terminal")
static HFONT	ghFont2;		// Aktuelle "Schönschrift" für outtextxy 
static COLORREF rgbpalette[MAXCOLORS+1]={
 0x000000L,0xC00000L,0x00C000L,0xC0C000L,
 0x0000C0L,0xC000C0L,0x0080C0L,0xC0C0C0L,
 0x808080L,0xFF8080L,0x80FF80L,0xFFFF80L,
 0x8080FFL,0xFF80FFL,0x80FFFFL,0xFFFFFFL};
static char *FontNames[5]={
 "Terminal",				// DEFAULT_FONT
 "Times New Roman",			// TRIPLEX_FONT
 "Small Fonts",				// SMALL_FONT
 "Arial",				// SANS_SERIF_FONT
 "MS Gothic"};				// GOTHIC_FONT


void far pascal yield(void) {
 MSG M;

 while (PeekMessage(&M, 0, 0, 0, PM_REMOVE)) {
  if (M.message==WM_QUIT) exit(255);
  TranslateMessage(&M);
  DispatchMessage(&M);
 }
}

void far pascal setwindowtitle(char far*title) {
 lstrcpyn(_WindowTitle,title,sizeof(_WindowTitle));
 SetWindowText(CrtWindow,title);
}

/* von EasyWin gekupfert: */
HANDLE	__hInstance;
HANDLE	__hPrev;
int	__cmdShow;
extern WORD  (*__ReadBufFPtr)(char *Buffer, WORD Count);
extern void  (*__WriteBufFPtr)(char *Buffer, WORD Count);
char	_WindowTitle[80];
char *	_InactiveTitle="(Inactive %s)";
char *	_KillWarning="Program is running!\nKill now?\n\n"
		     "(You may loose data from this application)";
static char	KeyBuffer[16];
static int	KeyCount;
static POINT	CharSize={8,16};// Größe der Zeichenzelle in Pixel
static POINT	FontSize={8,12};// Größe des verwendeten Fonts in Pixel
static POINT	FontCenter;	// zur Zentrierung der Zeichen in Zeichenzellen
static int	caret=2;	// Höhe des Cursors in Pixel
static BOOL	Changed;
static BOOL	CanTerminate;	// TRUE wenn Bildschirm leer
static BOOL	Reading;	// Caret im Textmodus zeigen
BOOL	_AutoTracking=TRUE;
BOOL	_CheckEOF=TRUE;
BOOL	_CheckBreak=TRUE;
BOOL	_DirectDraw=TRUE;
POINT	_Cursor;		// Schreibmarken-Position in Zeichenzellen
POINT	_ScreenSize;		// Fenster-Größe in Zeichenzellen

void pascal invalidate(void) {
 if (Changed) {
  InvalidateRect(CrtWindow,NULL,FALSE);
  Changed=FALSE;
 }
}

BOOL _KeyPressed(void) {
 invalidate();
//  CanTerminate=FALSE;		// Wenn etwas geschrieben wurde, Fenster nicht schließen!
 yield();
 return KeyCount;
}

static HANDLE cdecl gdiproc(FARPROC func,int argc,...) {
/* Allgemeine Kapsel von GDI-Zeichenfunktionen
 * argc enthält die Anzahl der folgenden int-Argumente
 * LONG- und FAR*-Argumente müssen in der Reihenfolge HIGH-LOW (verdreht)
 * übergeben werden
 *
 * Besondere Bits von argc:
 * Bit15: Keine sichtbaren Veränderungen, verzögertes blt wird unterdrückt.
 * Bit14: Bild ist leer, beendendes Programm darf das Fenster schließen
 *
 * Returnwert ist der von ghActiveDC, also von der Hintergrund-Bitmap
 * 
 * Wie zum Geier schreibt man in diesem Inline-Assembler ein Unterprogramm???
 */
 HANDLE retu;
 asm{ 	cld
	mov	ax,[ghActiveDC]
	 push	ax
	 lea	si,argc
	 lodsw
         mov	ah,0
	 xchg	cx,ax
         jcxz	l2
 }l1: asm{
	 lodsw
	 push	ax
	 loop	l1
 }l2: asm{
	 call	[func]
        mov	[retu],ax
	mov	ax,[visualpage]
	cmp	ax,[activepage]
        jne	exi		//niemals auf den Bildschirm!
	cmp	[_DirectDraw],0
	jz	le		//verzögert bltten
	mov	ax,[ghScreenDC]
 }sub: asm{
	 push	ax
	 lea	si,argc
	 lodsw
         mov	ah,0
	 xchg	cx,ax
         jcxz	l4
 }l3: asm{
	 lodsw
	 push	ax
	 loop	l3
 }l4: asm{
	 call	[func]
        jmp	exi
 }le:
  if (!(argc&0x8000)) Changed=TRUE;	//nur wenn zeichnende Operation
 exi:
 if (!(argc&0xC000)) CanTerminate=FALSE;// Wenn zeichnend und nicht leer 
 return retu;
}

static void _ShowCursor(void) {
/* Action nur im Textmodus! */
 if (gInGraphMode) return;
 CreateCaret(CrtWindow,0,CharSize.x*scale,caret*scale);
 SetCaretPos(_Cursor.x*CharSize.x*scale,((_Cursor.y+1)*CharSize.y-caret)*scale);
 ShowCaret(CrtWindow);
}

static void _HideCursor(void) {
/* Action nur im Textmodus! */
 if (gInGraphMode) return;
 HideCaret(CrtWindow);
 DestroyCaret();
}

static void _TrackCursor(void) {
/* Keine Action bei Fenster ohne Rollbalken */
}

int _ReadKey(void) {
 int readkey;

 _TrackCursor();
 if (!_KeyPressed()) {
  Reading=TRUE;  if (GetFocus()==CrtWindow) _ShowCursor();
  do; while (!_KeyPressed());
  Reading=FALSE; if (GetFocus()==CrtWindow) _HideCursor();
 }
 readkey=KeyBuffer[0];
 --KeyCount;
 memmove(KeyBuffer,KeyBuffer+1,KeyCount);
 return readkey;
}

static void pascal NewLine(void) {
 _Cursor.x=0;
 _Cursor.y++;
 if (_Cursor.y>=_ScreenSize.y) {
  _Cursor.y--;
  gdiproc((FARPROC)ScrollDC,10,0,-CharSize.y,0,0,0,0,0,0,0,0);
  gdiproc((FARPROC)PatBlt,6,0,_Cursor.y*CharSize.y,width,CharSize.y,
    HIWORD(BLACKNESS),LOWORD(BLACKNESS));
 }
}

static void pascal textout(char*buf,int slen) {
 POINT curs;
 RECT R;

 curs.x=_Cursor.x*CharSize.x;
 curs.y=_Cursor.y*CharSize.y;
 SetRect(&R,curs.x,curs.y,curs.x+CharSize.x*slen,curs.y+CharSize.y);
 if (!buf) slen=0;
 gdiproc((FARPROC)ExtTextOut,10,curs.x+FontCenter.x,curs.y+FontCenter.y,
   ETO_OPAQUE,HIWORD(&R),LOWORD(&R),_SS,LOWORD(buf),slen,0,0);
}

static void pascal PutText(char*buf,int slen) {
 textout(buf,slen);
 _Cursor.x+=slen;
 if (_Cursor.x>=_ScreenSize.x) NewLine();
}

void _WriteBuf(char *Buffer, WORD Count) {
// int slen;

/* {
  char b2[256];
  wvsprintf(b2,"CharSize=%d:%d",&CharSize);
  MessageBox(CrtWindow,b2,Buffer,0);
 } */
 gdiproc((FARPROC)SaveDC,0x8000);		//Viewport retten
 gdiproc((FARPROC)SetViewportOrg,0x8002,0,0);
 gdiproc((FARPROC)SelectClipRgn,0x8001,0);	//Falls vorhanden, Clipping weg
 while (Count>0) {
  switch (*Buffer) {
   case 13:	NewLine(); break;
   case 10:	break;
   case  9: {
    PutText(NULL,8-_Cursor.x%8);		//1..8 Leerzeichen
   }break;
   case  8: if (_Cursor.x>0) {
    --_Cursor.x;
    PutText(NULL,1);
    --_Cursor.x;
   }break;
   case  7:	MessageBeep(0); break;
   default:	PutText(Buffer,1);
  }
  ++Buffer;
  --Count;
 }
 if (_AutoTracking) _TrackCursor();
 gdiproc((FARPROC)RestoreDC,0x8001,-1);
}

void _WriteChar(char Ch) {
 _WriteBuf(&Ch,1);
}

WORD _ReadBuf(char *Buffer, WORD Count) {
/* realisiert einen einfachen Zeileneditor */
 unsigned char Ch;
 WORD I;

 I=0;
 do {
  Ch = _ReadKey();
  if (Ch==8) {		//Rückschritt
   if (I > 0) {
    --I;
    _WriteChar(8);
   }
  }else if (Ch>=32) {
   if (I<Count) {
    Buffer[I++]=Ch;
    _WriteChar(Ch);
   }
  }
 }while (!((Ch==13) || (_CheckEOF && (Ch==26))));
 if (I<Count-2) {
  Buffer[I++]=Ch;
  if (Ch==13) {
   Buffer[I++] = 10;
   _WriteChar(13);
  }
 }
 _TrackCursor();
 return I;
}

void _CursorTo(int x, int y) {
 _Cursor.x=max(0,min(x,_ScreenSize.x-1));
 _Cursor.y=max(0,min(y,_ScreenSize.y-1));
}

/* Ende EasyWin-gekupferte Sachen */

#define selobj(obj) gdiproc((FARPROC)SelectObject,0x8001,(obj))
#define setcol(proc,c) {COLORREF farbe=(c);\
 gdiproc((FARPROC)(proc),0x8002,HIWORD(farbe),LOWORD(farbe));}

static void pascal CheckPageLoaded(int page) {
/* Bitmap-Seiten werden bei Bedarf erzeugt */
 if (!pages[page]) {
  pages[page]=CreateCompatibleBitmap(ghScreenDC,width,height);
 }
}

static void pascal SetPages(int x,int y,int z,int c) {
/* erzeugt z Bitmap-Ebenen der Größe x*y der Farbtiefe c wie gewünscht */
 int i;
 if (ghNullBitmap) {
  SelectObject(ghActiveDC,ghNullBitmap);
  if (activepage!=visualpage) SelectObject(ghVisualDC,ghNullBitmap);
 }
 for (i=0; i<numpages; i++) {
  if (pages[i] && DeleteObject(pages[i])) pages[i]=0;
  if (i<z) pages[i]=CreateCompatibleBitmap(ghScreenDC,x,y);
 }
 activepage=0;
 visualpage=0;
 width=x;
 height=y;
 numpages=z;
 colors=c;
 if (z) {
  CheckPageLoaded(0);
  ghNullBitmap=SelectObject(ghActiveDC,pages[0]);	// 1. Ebene aktiv
/* Dummerweise kann man eine Bitmap nicht gleich in zwei Kontexte stellen! */
  clearviewport();
  SetWindowPos(CrtWindow,0,0,0,10000,10000,SWP_NOMOVE|SWP_NOZORDER);
 }
 FontCenter.x=(CharSize.x-FontSize.x)/2;
 FontCenter.y=(CharSize.y-FontSize.y)/2;
}

void far pascal internal_initgraph(int x, int y, int z, int c) {
 int i;
 gAspect.x=GetDeviceCaps(ghScreenDC,ASPECTX);
 gAspect.y=GetDeviceCaps(ghScreenDC,ASPECTY);
 gDefaultPalette.size=MAXCOLORS+1;
 for (i=0; i<=MAXCOLORS; i++) gDefaultPalette.colors[i]=i;
 gPalette=gDefaultPalette;
 SetPages(x,y,z,c);
 graphdefaults();
 gInGraphMode=TRUE;	// kein Cursor!
}

static HANDLE pascal ChangeGdiObject(HANDLE*ghandle,HANDLE nhandle) {
 HANDLE ret=selobj(nhandle);
 if (*ghandle==ret) DeleteObject(ret);
 *ghandle=nhandle;
 return ret;
}

static void pascal PenChange(void) {
//aufzurufen bei Änderung der Linienart, Linienstärke oder Vordergrundfarbe
 static int PenStyles[5]={
   PS_SOLID,PS_DOT,PS_DASHDOT,PS_DASH,PS_DASHDOTDOT};	//upattern nicht umsetzbar!
 ChangeGdiObject(&ghPen,CreatePen(
   PenStyles[gLineSettings.linestyle],
   gLineSettings.thickness,
   rgbpalette[gPalette.colors[gColor]]));
}

static void pascal CheckFont2(void) {
//Test und Laden Schönschrift-Font
/* Sehr wacklige Angelegenheit:
   Sehen die Fonts passend aus?
   Stimmen Größen und Abstände?
   UserCharSize sollte eingebaut werden! */
 if (!ghFont2) {
  if (gTextSettings.font) {
   ghFont2=CreateFont(gTextSettings.charsize*8,0/*gTextSettings.charsize*4*/,
   gTextSettings.direction?900:0,0,700,0,0,0,
   ANSI_CHARSET,0,0,0,0,
   FontNames[gTextSettings.font]);
  }else{
   ghFont2=CreateFont(8,8,0,
   gTextSettings.direction?900:0,0,0,0,0,
   OEM_CHARSET,0,0,0,0,
   FontNames[0]);
  }
 }
}

static void pascal Font2Change(void) {
//Bei Veränderungen den geladenen Font verwerfen
 if (ghFont2) {
  DeleteObject(ghFont2);
  ghFont2=0;
 }
}

static BOOL pascal NeedConvert(LPCSTR s) {
 if (!s) return FALSE;
 for (;*s;s++) {
  if ((BYTE)(*s)>=0x80) return TRUE;
 }
 return FALSE;
}

static void pascal MakeGdiObjects(void) {
 ghoPen=  ChangeGdiObject(&ghPen,  CreatePen(PS_SOLID,1,rgbpalette[15]));
 ghoBrush=ChangeGdiObject(&ghBrush,CreateSolidBrush(rgbpalette[15]));
 ghoFont= ChangeGdiObject(&ghFont, CreateFont(FontSize.y,FontSize.x,
   0,0,0,0,0,0,OEM_CHARSET,0,0,0,0,FontNames[0]));
 setcol(SetTextColor,rgbpalette[7]);	// grau wie BIOS
 setcol(SetBkColor,rgbpalette[0]);	// schwarz wie BIOS
 gdiproc(SetBkMode,0x8001,OPAQUE);
 _ScreenSize.x=width/CharSize.x;
 _ScreenSize.y=height/CharSize.y;
 gViewport.right=width-1;
 gViewport.bottom=height-1;
 gViewport.clip=1;
}

static void pascal DestroyGdiObjects(void) {
 ChangeGdiObject(&ghFont, ghoFont);
 ChangeGdiObject(&ghBrush,ghoBrush);
 ChangeGdiObject(&ghPen,  ghoPen);
 Font2Change();
}

/********************************************
 * Ersatz für EASYWIN- und CONIO-Funktionen *
 ********************************************/

void textmode(int newmode) {
 if (newmode==-1) return;
 FontSize.x=CharSize.x=8;
 FontSize.y=12;
 CharSize.y=16;
 _ScreenSize.y=25;
 if (newmode&64) {
  _ScreenSize.y=50;
  FontSize.y=CharSize.y=8;
  newmode&=~64;
 }
 _ScreenSize.x=80;
 switch (newmode) {
  case 0:
  case 1: _ScreenSize.x=40;
  FontSize.x=FontSize.y=CharSize.x=CharSize.y=16;
 }
 ChangeGdiObject(&ghFont,CreateFont(FontSize.y,FontSize.x,0,0,0,0,0,0,
   OEM_CHARSET,0,0,0,0,FontNames[0]));
 SetPages(_ScreenSize.x*CharSize.x,_ScreenSize.y*CharSize.y,1,16);
 gInGraphMode=FALSE;
}

void gotoxy(int x, int y) { _CursorTo(x-1,y-1); }

void clreol(void) {
 textout(NULL,_ScreenSize.x-_Cursor.x);
}

void clrscr(void) {
 gdiproc((FARPROC)PatBlt,0x4006,0,0,width,height,
   HIWORD(BLACKNESS),LOWORD(BLACKNESS));
 CanTerminate=TRUE;		// Da ist nichts mehr zu sehen
}

void _setcursortype(int cur_t) {
 switch (cur_t) {
  case 0: caret=0;		// _NOCURSOR
  case 1: caret=CharSize.y;	// _SOLIDCURSOR
  case 2: caret=2;		// _NORMALCURSOR
 }
}

void _InitEasyWin(void) {}	// gar nichts tun!

/************************************
 * Hilfsfunktionen für GRAPHICS.LIB *
 ************************************/

static unsigned char sintab[91]={
 0, 4, 9, 13, 18, 22, 27, 31, 35, 40, 44, 49, 53, 57, 62, 66, 70,
 75, 79, 83, 87, 91, 96, 100, 104, 108, 112, 116, 120, 124, 127, 131, 135,
 139, 143, 146, 150, 153, 157, 160, 164, 167, 171, 174, 177, 180, 183, 186,
 190, 192, 195, 198, 201, 204, 206, 209, 211, 214, 216, 219, 221, 223, 225,
 227, 229, 231, 233, 235, 236, 238, 240, 241, 243, 244, 245, 246, 247, 248,
 249, 250, 251, 252, 253, 253, 254, 254, 254, 255, 255, 255, 255};

static int pascal isin(int angle) {
 int negflag,y;
 angle%=360;
 negflag=angle>=180;
 if (negflag) angle-=180;		// Quadranten III und IV
 if (angle>90) angle=180-angle;		// Quadranten II und IV
 y=sintab[angle];
 if (negflag) y=-y;
 return y;
}

static int pascal icos(int angle) {
 return isin(angle+90);
}

int far pascal _ret(int x) {
 return x;
}

/*******************************************
 * Funktionsnachbildungen der GRAPHICS.LIB *
 *******************************************/

void far arc(int x, int y, int stangle, int endangle, int radius) {
 ellipse(x,y,stangle,endangle,radius,MulDiv(radius,gAspect.y,gAspect.x));
}

#pragma argsused
void far cdecl bar(int left, int top, int right, int bottom) {
 right++;
 bottom++;
 gdiproc((FARPROC)FillRect,3,HIWORD((LPRECT)&left),LOWORD((LPRECT)&left),
   ghBrush);
}

void far bar3d(int left, int top, int right, int bottom, int depth,
  int topflag) {
 gdiproc((FARPROC)Rectangle,4,left,top,right+1,bottom+1);
 gdiproc((FARPROC)SaveDC,0x8000);		//Position retten
 moveto(right,top);
 lineto(right+depth,top-depth/2);
 lineto(right+depth,bottom-depth/2);
 lineto(right,bottom);
 if (topflag) {
  moveto(right+depth,top-depth/2);
  lineto(left+depth,top-depth/2);
  lineto(left,top);
 }
 gdiproc((FARPROC)RestoreDC,0x8001,-1);
}

void far circle(int x, int y, int radius) {
 POINT rad;

 rad.x=radius;
 rad.y=MulDiv(radius,gAspect.y,gAspect.x);
 selobj(GetStockObject(HOLLOW_BRUSH));
 gdiproc((FARPROC)Ellipse,4,x-rad.x,y-rad.y,x+rad.x+1,y+rad.y+1);
 selobj(ghBrush);
}

void far cleardevice(void) {
 setviewport(0,0,getmaxx(),getmaxy(),0);
 clearviewport();
}

void far clearviewport(void) {
 clrscr();
 moveto(0,0);
}

void far closegraph() {
 gGraphDriver=0;
 textmode(3);
}

void far detectgraph(int far *graphdriver, int far *graphmode) {
 *graphdriver=VGA;
 *graphmode=VGAHI;
}

void far drawpoly(int c, const int far *p) {
 gdiproc((FARPROC)Polyline,3,HIWORD(p),LOWORD(p),c);
}

void far ellipse(int x, int y, int stang, int endang, int xr, int yr) {
 gArcCoords.x=x;
 gArcCoords.y=y;
 gArcCoords.xstart=x+MulDiv(icos(stang),xr,255);
 gArcCoords.ystart=y-MulDiv(isin(stang),yr,255);
 gArcCoords.xend=x+MulDiv(icos(endang),xr,255);
 gArcCoords.yend=y-MulDiv(isin(endang),yr,255);
 gdiproc((FARPROC)Arc,8,x-xr,y-yr,x+xr+1,y+yr+1,
   gArcCoords.xstart,gArcCoords.ystart,gArcCoords.xend,gArcCoords.yend);
}

void far fillellipse(int x, int y, int xr, int yr) {
 gdiproc((FARPROC)Ellipse,4,x-xr,y-yr,x+xr+1,y+yr+1);
}

void far fillpoly(int c, const int far *p) {
 gdiproc((FARPROC)Polygon,3,HIWORD(p),LOWORD(p),c);
}

void far floodfill(int x, int y, int b) {
//kann das auf der Zeichenfläche gemacht werden?
 COLORREF farbe;
 if ((unsigned)b>getmaxcolor()) return;
 farbe=rgbpalette[gPalette.colors[b]];
 gdiproc((FARPROC)FloodFill,4,x,y,HIWORD(farbe),LOWORD(farbe));
}

void far getarccoords(struct arccoordstype far *arccoords) {
 *arccoords=gArcCoords;
}

void far getaspectratio(int far *xasp, int far *yasp) {
 *xasp=gAspect.x;
 *yasp=gAspect.y;
}

int far getbkcolor(void)	{ return gBkColor; }
int far getcolor(void)		{ return gColor; }
struct palettetype far * far getdefaultpalette(void) {
				  return &gDefaultPalette; }
char *far getdrivername(void)	{ return gDriverName; }
void far getfillpattern(char far *pattern) {
 hmemcpy(pattern,gFillPattern[USER_FILL],8);
}
void far getfillsettings (struct fillsettingstype far *fillinfo) {
 *fillinfo=gFillSettings;
}
int far getgraphmode(void)	{ return gGraphMode; }

void far getimage(int left, int top, int right, int bottom,
  void far *bitmap) {
 HBITMAP bm;
 HDC dc;
 int w,h;

 w=right+1-left; h=bottom+1-top;
 bm=CreateCompatibleBitmap(ghScreenDC,w,h);
 dc=CreateCompatibleDC(ghScreenDC);
 SelectObject(dc,bm);
 BitBlt(dc,0,0,w,h,ghActiveDC,left,top,SRCCOPY);
 SelectObject(dc,ghNullBitmap);
 DeleteDC(dc);
 ((LPWORD)bitmap)[0]=w;
 ((LPWORD)bitmap)[1]=h;
 ((LPWORD)bitmap)[2]=bm;
}

void far getlinesettings(struct linesettingstype far *lineinfo) {
 *lineinfo=gLineSettings;
}

int far getmaxcolor(void)	{ return 15;}
int far getmaxmode(void)	{ return 2;}

#pragma argsused
char * far getmodename(int mode_number) {
 static char buf[16];
 wsprintf(buf,"%d x %d WIN",width,height);
 return buf;
}

int far getmaxx(void) {return width-1;}
int far getmaxy(void) {return height-1;}

void far getmoderange(int graphdriver, int far* lowmode, int far* himode) {
//So ziemlich jeder Modus wird hier unterstützt
 if (graphdriver) {
  *lowmode=0;
  *himode=2;
 }
}

void far getpalette(struct palettetype far *palette) {
 *palette=gPalette;
}

int far getpalettesize(void)		{ return gPalette.size; }

unsigned far getpixel(int x, int y) {
 COLORREF c;
 c=GetPixel(ghActiveDC,x,y);
 for (x=0; x<=MAXCOLORS; x++) if (c==rgbpalette[x]) return x;
 return 0;
}


void far gettextsettings(struct textsettingstype far* texttypeinfo) {
 *texttypeinfo=gTextSettings;
}

void far getviewsettings(struct viewporttype far* viewport) {
 *viewport=gViewport;
}

int far getx(void) {return (int)(LOWORD(GetCurrentPosition(ghActiveDC)));}
int far gety(void) {return (int)(HIWORD(GetCurrentPosition(ghActiveDC)));}

void far graphdefaults(void) {
 setviewport(0,0,getmaxx(),getmaxy(),0);
 moveto(0,0);
 gPalette=gDefaultPalette;
 setcolor(15);
 setbkcolor(0);
 setfillstyle(SOLID_FILL,15);
 settextjustify(LEFT_TEXT,TOP_TEXT);
 settextstyle(DEFAULT_FONT,HORIZ_DIR,1);
}

char *far grapherrormsg(int errorcode) {
 static char* msg[]={
    "Ok","NoInitGraph","NotDetected","FileNotFound",
    "InvalidDriver","NoLoadMem","NoScanMem","NoFloodMem",
    "FontNotFound","NoFontMem","InvalidMode","Error",
    "IOerror","InvalidFont","InvalidFontNum","InvalidDeviceNum",
    "InvalidVersion"};
 errorcode=-errorcode;
 if ((unsigned)errorcode>18) return NULL;
 return msg[errorcode];
}

int far graphresult(void) {
 register int ret=gGraphResult;
 gGraphResult=0;
 return ret;
}

#pragma argsused
void far initgraph(int far *graphdriver,
  int far *graphmode, const char far *pathtodriver) {
 if (*graphdriver==DETECT) detectgraph(graphdriver,graphmode);
 gGraphDriver=*graphdriver;
 setgraphmode(*graphmode);
}

void far pascal initgraph2(int x, int y, int z, int c) {
 gGraphDriver=UNIVERSAL;		// alles mögliche
 internal_initgraph(x,y,z,c);
}

#pragma argsused
void far _cdecl line(int x1, int y1, int x2, int y2) {
 gdiproc((FARPROC)Polyline,3,HIWORD((LPPOINT)&x1),LOWORD((LPPOINT)&x1),2);
}

void far linerel(int x, int y) {
 lineto(getx()+x,gety()+y);
}

void far lineto(int x, int y) {
 gdiproc((FARPROC)LineTo,2,x,y);
}

void far moverel(int x, int y) {
 moveto(getx()+x,gety()+y);
}

void far moveto(int x, int y) {
 gdiproc((FARPROC)MoveTo,0x8002,x,y);
}

void far outtext(const char far *textstring) {
 outtextxy(0x8000U,0x8000U,textstring);	// Spezial-Code
}

void far outtextxy(int x, int y, const char far *textstring) {
 static WORD horzalign[3]={TA_LEFT,TA_CENTER,TA_RIGHT};
 static WORD vertalign[3]={TA_BOTTOM,TA_BASELINE,TA_TOP}; //TA_VCENTER gibt's nicht
 char near*ansi=NULL;
 WORD slen=lstrlen(textstring);

 CheckFont2();
 selobj(ghFont2);
 gdiproc((FARPROC)SetTextAlign,0x8001,
   ((unsigned)x==0x8000U && (unsigned)y==0x8000U ? TA_UPDATECP : TA_NOUPDATECP)
   | (gTextSettings.direction
   ? horzalign[gTextSettings.vert]  | vertalign[2-gTextSettings.horiz]
   : horzalign[gTextSettings.horiz] | vertalign[gTextSettings.vert]));
 gdiproc((FARPROC)SetBkMode,0x8001,TRANSPARENT);
 if (gTextSettings.font && NeedConvert(textstring)) {
  ansi=(char near*)LocalAlloc(LMEM_FIXED,slen);
  OemToAnsiBuff(textstring,ansi,slen);	//ohne Null, wird nicht gebraucht
  textstring=ansi;	//Zeiger umsetzen
 }
 gdiproc((FARPROC)TextOut,5,x,y,HIWORD(textstring),LOWORD(textstring),slen);
 if (ansi) LocalFree((HANDLE)ansi);
 gdiproc((FARPROC)SetBkMode,0x8001,OPAQUE);
 gdiproc((FARPROC)SetTextAlign,0x8001,0);
 selobj(ghFont);
}

void far pieslice(int x, int y, int stangle, int endangle, int radius) {
 gdiproc((FARPROC)Pie,8,x-radius,y-radius,x+radius+1,y+radius+1,
   x+icos(stangle),y-isin(stangle),x+icos(endangle),y-isin(endangle));
}

void far putimage(int left, int top, const void far *bitmap, int op) {
 int w=((LPWORD)bitmap)[0];
 int h=((LPWORD)bitmap)[1];
 HBITMAP bm=((LPWORD)bitmap)[2];
 HDC dc=CreateCompatibleDC(ghScreenDC);
 static DWORD rops[]={SRCCOPY,SRCINVERT,SRCPAINT,SRCAND,NOTSRCCOPY};

 if ((unsigned)op>=4) return;
 SelectObject(dc,bm);
 gdiproc((FARPROC)BitBlt,9,left,top,w,h,dc,0,0,HIWORD(rops[op]),LOWORD(rops[op]));
 SelectObject(dc,ghNullBitmap);
 DeleteDC(dc);
}

void far putpixel(int x, int y, int c) {
 COLORREF farbe;
 if ((unsigned)c>MAXCOLORS) return;
 farbe=rgbpalette[gPalette.colors[c]];
 gdiproc((FARPROC)SetPixel,4,x,y,HIWORD(farbe),LOWORD(farbe));
}

void far rectangle(int left, int top, int right, int bottom) {
 selobj(GetStockObject(HOLLOW_BRUSH));
 gdiproc((FARPROC)Rectangle,4,left,top,right+1,bottom+1);
 selobj(ghBrush);
}

void far restorecrtmode(void) {
// gGraphDriver=0;
 textmode(3);
}

void far sector(int x, int y, int stang, int endang, int xr, int yr) {
 gdiproc((FARPROC)Pie,8,x-xr,y-yr,x+xr+1,y+yr+1,
   x+icos(stang),y-isin(stang),x+icos(endang),y-isin(endang));
}

void far setactivepage(int page) {
 BOOL already_there;
 if ((unsigned)page>=(unsigned)numpages) return;
 if (page==activepage) return;
 already_there=pages[page];
 CheckPageLoaded(page);
 activepage=page;
 SelectObject(ghActiveDC,pages[page]);
 if (!already_there) {
  PatBlt(ghActiveDC,0,0,width,height,BLACKNESS);
 }
}

void far setallpalette(const struct palettetype far *palette) {
 gPalette=*palette;
}

void far setaspectratio(int xasp, int yasp) {
 gAspect.x=xasp;
 gAspect.y=yasp;
}

void far setbkcolor(int color) {
 if ((unsigned)color>getmaxcolor()) return;
 gBkColor=color;
 setcol(SetBkColor,rgbpalette[gPalette.colors[color]]);
}

void far setcolor(int color) {
 if ((unsigned)color>getmaxcolor()) return;
 gColor=color;
 setcol(SetTextColor,rgbpalette[gPalette.colors[color]]);
 PenChange();
}

void far setfillpattern(const char far *upattern, int color) {
 hmemcpy(gFillPattern[USER_FILL],upattern,8);
 setfillstyle(USER_FILL,color);
}

void far setfillstyle(int pattern, int color) {
 HBRUSH br;
 static int HatchIndex[]={HS_HORIZONTAL,HS_FDIAGONAL,HS_FDIAGONAL,HS_BDIAGONAL,HS_BDIAGONAL,
   HS_CROSS,HS_DIAGCROSS,HS_VERTICAL,HS_CROSS,HS_CROSS};

 gFillSettings.pattern=pattern;
 gFillSettings.color=color;
 switch (gFillSettings.pattern) {
  case EMPTY_FILL: br=GetStockObject(HOLLOW_BRUSH); break;
  case SOLID_FILL: br=CreateSolidBrush(rgbpalette[gPalette.colors[gFillSettings.color]]); break;
  case USER_FILL: {
   HBITMAP bm;
   WORD bits[8];
   BYTE *bp;
   int i;
   for (bp=gFillPattern[gFillSettings.pattern],i=0; i<8; i++) bits[i]=*bp++;
   bm=CreateBitmap(8,8,1,1,bits);
   br=CreatePatternBrush(bm);
   DeleteObject(bm);
  }break;
  default: br=CreateHatchBrush(HatchIndex[gFillSettings.pattern-LINE_FILL],
    rgbpalette[gPalette.colors[color]]);
 }
 ChangeGdiObject(&ghBrush,br);
}

#pragma argsused
void far setgraphmode(int mode) {
 static int vgalines[3]={200,350,480};
 if (!gGraphDriver) return;
 if ((unsigned)mode>2) return;
 gGraphMode=mode;
 internal_initgraph(640,vgalines[mode],mode<2?2:1,MAXCOLORS+1);
}

#pragma argsused
void far setlinestyle(int linestyle, unsigned upattern, int thickness) {
 if ((unsigned)linestyle>USERBIT_LINE) return; 
 gLineSettings=*(struct linesettingstype*)&linestyle;
 PenChange();
}

void far setpalette(int colornum, int color) {
 if ((unsigned)colornum>=gPalette.size) return;
 if ((unsigned)color>getmaxcolor()) return;
 gPalette.colors[colornum]=color;
}

void far setrgbpalette(int colornum, int red, int green, int blue) {
 if ((unsigned)colornum>=gPalette.size) return;
 ((RGBQUAD*)(&rgbpalette[colornum]))->rgbRed=red;
 ((RGBQUAD*)(&rgbpalette[colornum]))->rgbGreen=green;
 ((RGBQUAD*)(&rgbpalette[colornum]))->rgbBlue=blue;
 //normalerweise und nur bei 256-Farben-Displays möglich: RealizePalette()
 //Paletten-Animation ist, weil selten verwendet, nicht vorgesehen.
}

void far settextjustify(int horiz, int vert) {
 if ((unsigned)horiz>2) return;
 if ((unsigned)vert>2) return;
 gTextSettings.horiz=horiz;
 gTextSettings.vert=vert;
 Font2Change();
}

void far settextstyle(int font, int direction, int charsize) {
 gTextSettings.font=font<5?font:0;
 gTextSettings.direction=direction;
 gTextSettings.charsize=charsize;
 Font2Change();
}

#pragma argsused
void far setusercharsize(int multx, int divx, int multy, int divy) {
 gUserCharSize=*(struct usercharsizetype*)&multx;
 Font2Change();
}

void far cdecl setviewport(int left, int top, int right, int bottom, int clip) {
 HRGN hRegion;
 gdiproc((FARPROC)SelectClipRgn,0x8001,0);	// Falls vorhanden, Clipping entfernen
 gViewport=*(struct viewporttype*)&left;
 if (clip) {
  hRegion=CreateRectRgn(left,top,right+1,bottom+1);
  gdiproc((FARPROC)SelectClipRgn,0x8001,hRegion);
  DeleteObject(hRegion);
 }
 gdiproc((FARPROC)SetViewportOrg,2,left,top);
}

void far setvisualpage(int page) {
 if ((unsigned)page>=(unsigned)numpages) return;
 if (page==visualpage) return;
 CheckPageLoaded(page);
 if (activepage!=visualpage) SelectObject(ghVisualDC,ghNullBitmap);
 visualpage=page;
 if (activepage!=visualpage) SelectObject(ghVisualDC,pages[page]);
 Changed=TRUE;
 if (_DirectDraw) {
  invalidate();
  UpdateWindow(CrtWindow);	// Sofort aktualisieren
 }
}

void far setwritemode(int mode) {
 gdiproc((FARPROC)SetROP2,0x8001,mode ? R2_XORPEN : R2_COPYPEN);
}

long far pascal textextent(const char far *textstring) {
 long ret;
 CheckFont2();
 SelectObject(ghActiveDC,ghFont2);
 ret=GetTextExtent(ghActiveDC,textstring,lstrlen(textstring));
 SelectObject(ghActiveDC,ghFont);
 return ret;
}

int far textheight(const char far *textstring) {
 return HIWORD(textextent(textstring));
}

int far textwidth(const char far *textstring) {
 return LOWORD(textextent(textstring));
}

/********************************************
 * Hier geht's mit richtigem Windows weiter *
 ********************************************/

long far pascal GraphWndProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
 switch (Msg) {
  case WM_CREATE: {
   CrtWindow=Wnd;
   ghScreenDC=GetDC(Wnd);
   ghActiveDC=CreateCompatibleDC(ghScreenDC);
   ghVisualDC=CreateCompatibleDC(ghScreenDC);
   SetPages(640,400,1,16);	//mit VGA-Textmodus-Emulation geht's los!
   MakeGdiObjects();
  }break;

  case WM_SETFOCUS:  if (Reading) _ShowCursor(); break;
  case WM_KILLFOCUS: if (Reading) _HideCursor(); break;

  case WM_QUERYENDSESSION:
  case WM_CLOSE: {
   if (CanClose || MessageBox(Wnd,_KillWarning,_WindowTitle,
     MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2)==IDYES) break;
     			// DefWindowProc() schließt - nur bei WM_Close
  }return 0;		// nicht schließen!

  case WM_KEYDOWN: {
   if ((VK_PRIOR<=wParam && wParam<=VK_DOWN)
   || (VK_INSERT<=wParam && wParam<=VK_HELP)
   || (VK_F1<=wParam && wParam<=VK_SCROLL)) {
    if (KeyCount<sizeof(KeyBuffer)-1) {
     KeyBuffer[KeyCount++]='\0';
     KeyBuffer[KeyCount++]=LOBYTE(HIWORD(lParam));
    }
   }
  }break;

  case WM_GETMINMAXINFO: {
   RECT R;
   SetRect(&R,0,0,width*scale,height*scale);
   AdjustWindowRect(&R,WS_OVERLAPPEDWINDOW,FALSE);
   ((LPPOINT)(lParam))[4].x=R.right-R.left;
   ((LPPOINT)(lParam))[4].y=R.bottom-R.top;
  }break;

  case WM_CHAR: {
   if (_CheckBreak  && (wParam==3)) DestroyWindow(Wnd);
   if (KeyCount<sizeof(KeyBuffer)) {
    AnsiToOemBuff((LPSTR)&wParam,KeyBuffer+KeyCount++,1);	// convert the character!
   }
  }break;

  case WM_PAINT: {
   PAINTSTRUCT ps;
   HDC src_dc= activepage!=visualpage ? ghVisualDC : ghActiveDC;
   BeginPaint(Wnd,&ps);
   if (scale==1) BitBlt(ps.hdc,0,0,width,height,src_dc,0,0,SRCCOPY);
   else StretchBlt(ps.hdc,0,0,width*scale,height*scale,src_dc,0,0,width,height,SRCCOPY);
   EndPaint(Wnd,&ps);
  }return 0;

  case WM_DESTROY: {
   DestroyGdiObjects();
   SetPages(0,0,0,0);
   DeleteDC(ghActiveDC);
   DeleteDC(ghVisualDC);
   ReleaseDC(Wnd,ghScreenDC);
   PostQuitMessage(0);
  }break;
 }
 return DefWindowProc(Wnd,Msg,wParam,lParam);
}

static void pascal EndMessageLoop(void) {
/* zum Stehenlassen des inaktiven Fensters */
 MSG Msg;
 char Title[128];

 invalidate();
 wsprintf(Title,_InactiveTitle,(char far*)_WindowTitle);
 SetWindowText(CrtWindow,Title);
 CanClose=TRUE;
 while (GetMessage(&Msg,0,0,0)) {
  TranslateMessage(&Msg);
  DispatchMessage(&Msg);
 }
}

//Deklaration von main()
int main(int argc,char**argv,char**envp);

#pragma argsused
int pascal WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpCmdLine,
  int nCmdShow) {
 int ret;
 BOOL IsWin9x;
 char fontname[80];


 if (!hPrevInstance) {
  WNDCLASS wc;
  memset(&wc,0,sizeof(wc));
  wc.lpfnWndProc=GraphWndProc;
  wc.hInstance=hInstance;
  wc.hIcon=LoadIcon(0,IDI_APPLICATION);
  wc.hCursor=LoadCursor(0,IDC_ARROW);
  wc.lpszClassName="GRAPHWIN";
  RegisterClass(&wc);
 }
 GetModuleFileName(hInstance, _WindowTitle, sizeof(_WindowTitle));
 OemToAnsi(_WindowTitle, _WindowTitle);
 __ReadBufFPtr =_ReadBuf;
 __WriteBufFPtr=_WriteBuf;
 IsWin9x=HIBYTE(LOWORD(GetVersion()))>=95;
 if (IsWin9x) {
  ret=GetWindowsDirectory(fontname,sizeof(fontname)-7);
  if (fontname[ret-1]!='\\') fontname[ret++]='\\';
  lstrcpy(fontname+ret,"FONTS\\");
  ret+=6;
 }else{
  ret=GetSystemDirectory(fontname,sizeof(fontname)-1);
  if (fontname[ret-1]!='\\') fontname[ret++]='\\';
 }
 GetPrivateProfileString("386Enh","woafont","dosapp.fon",
   fontname+ret,sizeof(fontname)-ret,"SYSTEM.INI");
 AddFontResource(fontname);

 CreateWindow("GRAPHWIN",_WindowTitle,
   (WS_OVERLAPPEDWINDOW&~WS_MAXIMIZEBOX)|WS_VISIBLE,
   CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
   0,0,hInstance,NULL);

 ret=main(_argc,_argv,environ);
 if (CanTerminate) DestroyWindow(CrtWindow);
 EndMessageLoop();
 RemoveFontResource(fontname);
 return ret; 
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded