/*
 GNU Maverik - a system for managing display and interaction in 
               Virtual Environment applications.
 Copyright (C) 1999-2001 Advanced Interfaces Group

 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


 The authors can be contacted via:
 www   - http://aig.cs.man.ac.uk
 email - maverik@aig.cs.man.ac.uk
 mail  - Advanced Interfaces Group, Room 2.94, Computer Science Building, 
         University of Manchester, Manchester, M13 9PL, UK
*/


/* These are the only bits of Maverik we need. Keep interface to window
   manager as light as possible to make its easy for other implementation */

/* I'm beginning to think it would be easier just to #include "maverik.h" */

#ifdef __cplusplus
extern "C" {
#endif
void mav_gfxWindowOpen(int id, int x, int y, int w, int h, char *name, char *disp, int wmp, int sb, int qb, int ms, int ab, int stenb, int desta, int *wret, int *hret);
void mav_gfxWindowClose(int id);
void mav_gfxWindowSet(int id);
void mav_gfxWindowBuffersSwap(void);
void mav_gfxWindowResGet(int *x, int *y);
int  mav_gfxWindowEventGet(int *info);
int  mav_gfxWindowEventPeek(void);
int  mav_gfxWindowPointerGet(int id, int *x, int *y, int *rx, int *ry, int *buts);
void mav_gfxWindowPointerSet(int win, int x, int y);
int  mav_gfxWindowKeyGet(int key);
int  mav_gfxWindowFontSet(char *s, int font, int *width);
void mav_gfxWindowStringDisplay(char *s, int font);
void mav_moduleNew(char *fn(void));
int  mav_gfxModuleInit(void);
char *mav_gfxModuleID(void);
void mav_gfx3DfxModeSet(int fullscreen);
int  mav_gfx3DfxBoardSet(int bd);

typedef void (*MAV_frameFn)(void *);
void mav_frameFn2Add(MAV_frameFn);

#ifdef __cplusplus
}
#endif
#define MAX_WINS 10 /* Make sure the same or greater than equivalent in mav_kernel.h */
#define MAX_FONTS 10 /* Same as above */
#define MAX_DPYS 8
extern int mav_opt_bindTextures;
extern int mav_opt_shareContexts;
extern int mav_opt_maxTextures;
extern int mavlib_voodoo;
extern void *mavlib_dlh;
extern char *mav_gfx_vendor;
extern char *mav_gfx_renderer;
extern char *mav_gfx_version;

extern int mav_opt_output;

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* minimum tracking Y (over non-client height) */
#define MAVLIB_MIN_Y 2

#define MAVLIB_CLIPPER 1

#define INITGUID

#include <windows.h>

#include "mavlib_d3d.h"

/* 2d text info */
char mavlib_string_texts[100][1000];
int mavlib_string_xs[100];
int mavlib_string_ys[100];
COLORREF mavlib_string_colours[100];
int mavlib_string_fonts[100];
int mavlib_string_count= 0;

/* font handles */
HFONT mavlib_fonts[MAX_FONTS];

/* globals for window event handler */
int mavlib_win32EventInfo[50]; /* Event info stored here */
int mavlib_win32EventGood=0;   /* Indicates type of event */
int mavlib_win32Create=-1;     /* Indicates id of window for create event */
int mavlib_win32MouseCaught= 0; /* count of capture requests */
/* button press windows */
HWND mavlib_win32MouseWin[4]= {NULL, NULL, NULL, NULL};
int mavlib_win32MouseEventsPending= 0; /* for flushing button up events */

/* direct draw handles */
LPDIRECTDRAW7 mavlib_DD;
LPDIRECT3D7 mavlib_D3D;
LPDIRECT3DDEVICE7 mavlib_D3DDevice= NULL;
LPDIRECTDRAWSURFACE7 mavlib_D3DSurface;
LPDIRECTDRAWSURFACE7 mavlib_ZBuffer;
LPDIRECTDRAWSURFACE7 mavlib_backBuffer;
LPDIRECTDRAWCLIPPER mavlib_D3DClipper;
DDPIXELFORMAT mavlib_texRGB;
DDPIXELFORMAT mavlib_texRGBA;
DDPIXELFORMAT mavlib_tex1A;

HINSTANCE mavlib_ddrawDLL;

/* config file */
char mavlib_config_dd[100];
char mavlib_config_d3d[100];
int mavlib_config_w= MAVLIB_WIDTH;
int mavlib_config_h= MAVLIB_HEIGHT;
int mavlib_config_d= MAVLIB_BITS;
int mavlib_config_1a= 0;

/* options */
int mavlib_gfx_fullscreen= 0;
int mavlib_gfx_software= 0;
int mavlib_gfx_textureBits= 16;
int mavlib_gfx_aa= 0;
int mavlib_first_create= 1;

/* device caps */
int mavlib_got_zlesshsr= 0;

/* flag for between begin and end scene */
int mavlib_gfx_inScene= 0;

/* raster pos */
int mavlib_2d_x= 0;
int mavlib_2d_y= 0;

/* option from Maverik */
extern int mav_opt_fullscreen;

/* typedef for DirectDrawCreateEx */
typedef HRESULT WINAPI (*mavlib_ddfunctype) (GUID FAR *, LPVOID *, REFIID, IUnknown FAR *);

/* screen handle */
HINSTANCE mavlib_dpy;

/* windows event handler */
LONG WINAPI mavlib_winEventHandler(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam);

/* Data structure to store window and context of each MAV_window */

typedef struct {
  HDC hdc;
  HWND win;
  LPDIRECTDRAWSURFACE7 bb; /* each window has it's own back buffer surface */
  LPDIRECTDRAWSURFACE7 zb; /* and Z buffer surface */
  RECT screenRect; /* rectangles for buffer swapping blits */
  RECT viewRect;
  D3DMATRIX projMat;
  D3DMATRIX worldMat;
  int width;
  int height;
  int resized;
  int exposed;
  int mapped;
} MAVLIB_winhand;

MAVLIB_winhand mavlib_winhand[MAX_WINS];
int mavlib_currwin= 0;
int mavlib_wincount= 0;

/* function to calculate text width */
int mav_gfxStringLength (int win, char *s, int font)
{
  SIZE size;

/* make font current */
  SelectObject (mavlib_winhand[mavlib_currwin].hdc, mavlib_fonts[font]);

/* get size of text in current font */
  GetTextExtentPoint32 (mavlib_winhand[mavlib_currwin].hdc, s,
		strlen (s), &size);

/* only want x extent */
  return size.cx;
}

int mavlib_winlookup(HWND w)
{
  int i;
  
  for (i=0; i<MAX_WINS; i++) {
    if (mavlib_winhand[i].win==w) return i;
  }

  return -1;
}



/* Routines to initialise the graphics module */

char *mav_gfxModuleID(void)
{
  return "Graphics (D3D and Windows)";
}

void mavlib_gfxExit(void)
{
  int i;

fprintf (stderr, "gfx exit\n");

/* release font handles */
  for (i=0; i<MAX_FONTS; i++) {
    if (mavlib_fonts[i]) DeleteObject (mavlib_fonts[i]);
  }

/* release texture handles */
  for (i=0; i<GFX_MAX_TEXTURE; i++) {
    if (mavlib_textures[i]) {
      mavlib_textures[i]->lpVtbl->Release (mavlib_textures[i]);
    }
  }

/* D3D releases */

#ifdef MAVLIB_CLIPPER
/* clipper */
  if (!mavlib_gfx_fullscreen) {
    mavlib_D3DClipper->lpVtbl->Release (mavlib_D3DClipper);
  }
#endif

  for (i=0; i<MAX_WINS; i++) {
    if (mavlib_winhand[i].win) {
/* back buffer (windowed) */
      if (!mavlib_gfx_fullscreen) {
        mavlib_winhand[i].bb->lpVtbl->Release (mavlib_winhand[i].bb);
      }

/* Z buffer */
      mavlib_winhand[i].zb->lpVtbl->Release (mavlib_winhand[i].zb);
    }
  }

/* front buffer */
  mavlib_D3DSurface->lpVtbl->Release (mavlib_D3DSurface);

/* D3D device */
  mavlib_D3DDevice->lpVtbl->Release (mavlib_D3DDevice);

/* D3D handle */
  mavlib_D3D->lpVtbl->Release (mavlib_D3D);

/* DD handle */
  mavlib_DD->lpVtbl->Release (mavlib_DD);

/* ddraw library */
  FreeLibrary (mavlib_ddrawDLL);

  /* shut down windows nicely */
  for (i=0; i<MAX_WINS; i++) {
    if (mavlib_winhand[i].win) {
      ReleaseDC (mavlib_winhand[i].win, mavlib_winhand[i].hdc);
      DestroyWindow(mavlib_winhand[i].win);
    }
  }
}

void mavlib_readline (FILE *fp, char *str)
{
  char ch;
  int i= 0;

  ch= getc (fp);

  while (ch != '\n') {
    str[i]= ch;
    i ++;
    ch= getc (fp);
  }

  str[i]=(char) NULL;
}

/* typedef for DirectDrawEnumerateEx */
typedef HRESULT WINAPI (*mavlib_ddenumtype) (LPDDENUMCALLBACKEX, LPVOID, DWORD);

GUID mavlib_ddguid;
GUID *mavlib_pddguid= NULL;
GUID mavlib_d3dguid;
GUID *mavlib_pd3dguid= NULL;

HRESULT WINAPI mavlib_d3denumcb (LPSTR descstr, LPSTR namestr, LPD3DDEVICEDESC7 desc, LPVOID junk)
{
  if (!strcmp (namestr, mavlib_config_d3d)) {
/* user selected this GUID */
    memcpy (&mavlib_d3dguid, &desc->deviceGUID, sizeof (GUID));
    mavlib_pd3dguid= &mavlib_d3dguid;

/* check caps of this device */
    if ((desc->dwDevCaps & D3DDEVCAPS_TEXTURENONLOCALVIDMEM) ||
	(desc->dwDevCaps & D3DDEVCAPS_TEXTUREVIDEOMEMORY)) {
      mavlib_gfx_software= 0;
    } else {
      mavlib_gfx_software= 1;
    }

    if (!(desc->dpcTriCaps.dwRasterCaps &
		D3DPRASTERCAPS_ANTIALIASSORTINDEPENDENT)) {
      mavlib_gfx_aa= 0;
    } else {
      fprintf (stderr, "AA available - ");
      if (mavlib_gfx_aa) fprintf (stderr, "used\n");
      else fprintf (stderr, "not used\n");
    }

    if (desc->dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_ZBUFFERLESSHSR) {
      mavlib_got_zlesshsr= 1;
      fprintf (stderr, "Z less HSR\n");
    }
  }

  return D3DENUMRET_OK;
}


BOOL WINAPI mavlib_ddenumcb (GUID *pguid, LPSTR descstr, LPSTR namestr, LPVOID libvoid, HMONITOR hmon)
{
  mavlib_ddfunctype ddcreatefunc;
  HINSTANCE ddlib;

/* save this GUID */
  if (!strcmp (namestr, mavlib_config_dd)) {
/* user selected this GUID */
    if (pguid!=NULL) {
      memcpy (&mavlib_ddguid, pguid, sizeof (GUID));
      mavlib_pddguid= &mavlib_ddguid;
    }
  }

/* get the create function */
  ddlib= (HINSTANCE) libvoid;
  
  ddcreatefunc= (mavlib_ddfunctype) GetProcAddress (ddlib,
			"DirectDrawCreateEx");

  if (!ddcreatefunc) {
    fprintf (stderr, "Error: get function (DirectDrawCreateEx) failed\n");
    exit (1);
  }

/* create a DD interface */
  MAVD3DERR ((*ddcreatefunc) (pguid, (VOID **) &mavlib_DD, &IID_IDirectDraw7,
		NULL),
		"failed to get DD7 interface");

/* create a D3D interface */
  MAVD3DERR (mavlib_DD->lpVtbl->QueryInterface (mavlib_DD,
		&IID_IDirect3D7, (LPVOID *) &mavlib_D3D),
		"failed to get D3D7 interface");

/* enumerate the D3D devices */
  MAVD3DERR (mavlib_D3D->lpVtbl->EnumDevices (mavlib_D3D,
		mavlib_d3denumcb, NULL),
		"failed to enumerate D3D devices");

/* clean up */
  mavlib_D3D->lpVtbl->Release (mavlib_D3D);

  mavlib_DD->lpVtbl->Release (mavlib_DD);

  return TRUE;
}


/* Begin scene and end scene */

void mavlib_beginScene (void *ignored) {
  if (mavlib_D3DDevice->lpVtbl->BeginScene (mavlib_D3DDevice) != D3D_OK) {
	fprintf (stderr, "Warning: BeginScene failed\n");
  } else {
    mavlib_gfx_inScene= 1;
  }
}

void mavlib_endScene (void) {
  if (mavlib_gfx_inScene) {
    if (mavlib_D3DDevice->lpVtbl->EndScene (mavlib_D3DDevice) != D3D_OK) {
/* restore lost surfaces */
      MAVD3DWARN (mavlib_DD->lpVtbl->RestoreAllSurfaces (mavlib_DD),
		"end scene failed and no lost surfaces");
    }

    mavlib_gfx_inScene= 0;
  }
}

int mav_gfxModuleInit()
{
  /* Initialise data structure */
  int i;
  WNDCLASS wc;
  FILE *fp;
  char filename[256];
  HINSTANCE ddlib;
  mavlib_ddenumtype ddenumfunc;

/* parse config file */
  mavlib_config_dd[0]= (char) NULL;
  mavlib_config_d3d[0]= (char) NULL;

  sprintf (filename, "%s/.mavd3d_config", getenv("HOME"));
  fp= fopen (filename, "r");

  if (fp) {
    mavlib_readline (fp, mavlib_config_dd);
    mavlib_readline (fp, mavlib_config_d3d);
    fscanf (fp, "%d %d %d", &mavlib_config_w, &mavlib_config_h,
			&mavlib_config_d);
    fscanf (fp, "%d", &mavlib_gfx_textureBits);
    fscanf (fp, "%d %d %d", &mavlib_config_1a, &mavlib_gfx_aa,
			&mavlib_gfx_fullscreen);
    fclose (fp);
  }

/* enum available ddraw interfaces */
  ddlib= LoadLibrary ("DDRAW.DLL");
  if (!ddlib) {
    fprintf (stderr, "Error: Load library failed\n");
    exit (1);
  }

  ddenumfunc= (mavlib_ddenumtype) GetProcAddress (ddlib,
		"DirectDrawEnumerateExA");

  if (!ddenumfunc) {
    fprintf (stderr, "Error: failed to get enumerate function\n");
    exit (1);
  }

  MAVD3DERR ((*ddenumfunc) (mavlib_ddenumcb, (LPVOID) ddlib,
	DDENUM_ATTACHEDSECONDARYDEVICES|DDENUM_NONDISPLAYDEVICES),
	"failed to enumerate DD devices");

/* at this point mavlib_pddguid and mavlib_pd3dguid have pointers to */
/* the user selected GUIDs, or are NULL */

  FreeLibrary (ddlib);

/* reset window handles */
  for (i=0; i<MAX_WINS; i++) mavlib_winhand[i].win= (HWND) NULL;

/* reset font handles */
  for (i=0; i<MAX_FONTS; i++) mavlib_fonts[i]= NULL;

/* reset texture handles */
  for (i=0; i<GFX_MAX_TEXTURE; i++) mavlib_textures[i]= NULL;

  /* add the new module */
  mav_moduleNew(mav_gfxModuleID);  

  /* Open connection to display */  
  mavlib_dpy = GetModuleHandle(NULL);
  if (!mavlib_dpy) {
    fprintf (stderr, "Error: cannot connect to screen\n");
    exit (1);
  }

  /* Register class */
  wc.style= 0; /* CS_HREDRAW | CS_VREDRAW; */
  wc.lpfnWndProc= (WNDPROC) mavlib_winEventHandler;
  wc.cbClsExtra= 0;
  wc.cbWndExtra= 0;
  wc.hInstance= mavlib_dpy;
  wc.hIcon= 0;
  wc.hCursor= LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground= GetStockObject (BLACK_BRUSH);
  wc.lpszMenuName= NULL;
  wc.lpszClassName= "Maverik";
    
  if (!RegisterClass(&wc)) {
    fprintf (stderr, "Error: failed to register class\n");
    exit (1);
  }

/* need to add function that calls BeginScene to Frame2 */
  mav_frameFn2Add (mavlib_beginScene, NULL);

  return 1;
}



/* Routine to get the resolution of the display */

void mav_gfxWindowResGet(int *xres, int *yres)
{
  *xres= GetSystemMetrics(SM_CXSCREEN);
  *yres= GetSystemMetrics(SM_CYSCREEN);
}

void mavlib_createClipper (HWND hwnd)
{
#if MAVLIB_CLIPPER
/* get view clipper */
  MAVD3DERR (mavlib_DD->lpVtbl->CreateClipper (mavlib_DD, 0,
		&mavlib_D3DClipper, NULL),
		"failed to create clipper");

  MAVD3DERR (mavlib_D3DClipper->lpVtbl->SetHWnd (mavlib_D3DClipper,
		0, hwnd),
		"failed to set clipper hwnd");

  MAVD3DERR (mavlib_D3DSurface->lpVtbl->SetClipper (mavlib_D3DSurface,
		mavlib_D3DClipper),
		"failed to set surface clipper");
#endif
}

void mavlib_setView (int i)
{
  D3DVIEWPORT7 vp;

/* create viewport */
  vp.dwX= 0;
  vp.dwY= 0;
  vp.dwWidth= mavlib_winhand[i].viewRect.right -
			mavlib_winhand[i].viewRect.left;
  vp.dwHeight= mavlib_winhand[i].viewRect.bottom -
			mavlib_winhand[i].viewRect.top;
  vp.dvMinZ= 0.0;
  vp.dvMaxZ= 1.0;

  MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetViewport (mavlib_D3DDevice,
		&vp),
		"failed to set viewport");
}

/* Routine to set the current window */

void mav_gfxWindowSet(int i)
{
  int was_in_scene;

  if (mavlib_currwin != i) {
/* need to be out of a scene */
    was_in_scene= mavlib_gfx_inScene;
    if (mavlib_gfx_inScene)
      mavlib_endScene ();

/* get matrices */
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->GetTransform (mavlib_D3DDevice,
	D3DTRANSFORMSTATE_WORLD, &mavlib_winhand[mavlib_currwin].worldMat),
	"failed get world matrix");
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->GetTransform (mavlib_D3DDevice,
	D3DTRANSFORMSTATE_PROJECTION, &mavlib_winhand[mavlib_currwin].projMat),
	"failed get projection matrix");

/* set current window id */
    mavlib_currwin= i;

/* set matrices */
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTransform (mavlib_D3DDevice,
	D3DTRANSFORMSTATE_WORLD, &mavlib_winhand[mavlib_currwin].worldMat),
	"failed set world matrix");
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetTransform (mavlib_D3DDevice,
	D3DTRANSFORMSTATE_PROJECTION, &mavlib_winhand[mavlib_currwin].projMat),
	"failed set projection matrix");

/* set front, back and Z buffers */
    if (!mavlib_gfx_fullscreen) {
      MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderTarget (mavlib_D3DDevice,
	mavlib_winhand[i].bb, 0),
	"failed set render target");
    }

    mavlib_backBuffer= mavlib_winhand[i].bb;
    mavlib_ZBuffer= mavlib_winhand[i].zb;

/* set the viewport */
    mavlib_setView (i);

/* restore scene status */
    if (was_in_scene)
      mavlib_beginScene ();
  }
}

/* Routine to swap the buffers of the current window */

void mav_gfxWindowBuffersSwap(void)
{
  int i;
  HDC hdc;

/* call end scene */
  mavlib_endScene ();

/* draw any text */
  if (!mavlib_gfx_fullscreen) {
/* for windowed rendering, lock and draw text to the back buffer */
/* before buffer flip */
    if (mavlib_backBuffer->lpVtbl->GetDC (mavlib_backBuffer, &hdc) == DD_OK) {
      SetBkMode (hdc, TRANSPARENT);
      for (i=0; i<mavlib_string_count; i++) {
        SelectObject (hdc, mavlib_fonts[mavlib_string_fonts[i]]);
        SetTextAlign (hdc, TA_BOTTOM|TA_LEFT);
        SetTextColor (hdc, mavlib_string_colours[i]);
        ExtTextOut (hdc, mavlib_string_xs[i], mavlib_string_ys[i], 0, NULL, 
		mavlib_string_texts[i], strlen (mavlib_string_texts[i]), NULL);
      }
 
      mavlib_backBuffer->lpVtbl->ReleaseDC (mavlib_backBuffer, hdc);
    }
  }

  if (mavlib_gfx_fullscreen) {
/* just need to call Flip */
    MAVD3DERR (mavlib_D3DSurface->lpVtbl->Flip (mavlib_D3DSurface,
		NULL, DDFLIP_WAIT),
		"failed buffer flip");
  } else {
    int i;

/* need to swap for each window ... */
    for (i=1; i<=mavlib_wincount; i++) {
#if MAVLIB_CLIPPER
      MAVD3DERR (mavlib_D3DClipper->lpVtbl->SetHWnd (mavlib_D3DClipper,
		0, mavlib_winhand[i].win),
		"failed set clipper hwnd");
#endif

/* ... by blitting from the window's back buffer */
      MAVD3DWARN (mavlib_D3DSurface->lpVtbl->Blt (mavlib_D3DSurface,
		&(mavlib_winhand[i].screenRect),
		mavlib_winhand[i].bb, NULL, DDBLT_WAIT, NULL),
		"failed blt");
    }
  }

/* draw text */
  if (mavlib_gfx_fullscreen) {
/* for fullscreen, locking back buffer causes a seg fault so lock */
/* and draw direct to front buffer after buffer flip - GDI text */
/* drawing is fast enough that this shouldn't cause flicker problems */
    if (mavlib_D3DSurface->lpVtbl->GetDC (mavlib_D3DSurface, &hdc) == DD_OK) {
      SetBkMode (hdc, TRANSPARENT);
      for (i=0; i<mavlib_string_count; i++) {
        SelectObject (hdc, mavlib_fonts[mavlib_string_fonts[i]]);
        SetTextAlign (hdc, TA_BOTTOM|TA_LEFT);
        SetTextColor (hdc, mavlib_string_colours[i]);
        ExtTextOut (hdc, mavlib_string_xs[i], mavlib_string_ys[i], 0, NULL, 
		mavlib_string_texts[i], strlen (mavlib_string_texts[i]), NULL);
      }
 
      MAVD3DERR (mavlib_D3DSurface->lpVtbl->ReleaseDC (mavlib_D3DSurface, hdc),
			"failed release surface DC");
    }
  }

/* reset text string count */
  mavlib_string_count= 0;
}



/* Routine to read a 2D raster font and store as a display list (OpenGL only) */

int mav_gfxWindowFontSet(char *s, int i, int *width)
{
  int pos= 1;
  int weight= FW_NORMAL;
  BOOL italic= FALSE;
  int family= FF_SWISS;
  int height= -1;
  char typeface[100];
  int tf= 0;

/* release previous font */
  if (mavlib_fonts[i]) DeleteObject (mavlib_fonts[i]);

/* make a best guess at the font (the size at least should be ok) */
/* company */
  while (s[pos] != '-') pos ++;
  pos ++;

/* typeface */
  while (s[pos] != '-') {
    typeface[tf]= s[pos];
    tf ++;
    pos ++;
  }
  typeface[tf]= (char) NULL;
  pos ++;

/* weight */
  if (s[pos]=='b') weight= FW_BOLD;
  else if (s[pos]=='m') weight= FW_MEDIUM;
  while (s[pos] != '-') pos ++;
  pos ++;

/* slant */
  if (s[pos]=='i') italic= TRUE;
  if (s[pos]=='o' && s[pos+1] != 't')
    family= FF_MODERN;
  if (s[pos]=='r' && s[pos+1]=='-')
    family= FF_ROMAN;
  while (s[pos] != '-') pos ++;
  pos ++;

/* ignored */
  while (s[pos] != '-') pos ++;
  pos ++;

/* ignored */
  while (s[pos] != '-') pos ++;
  pos ++;

/* pixel height */
  if (s[pos] != '-' && s[pos] != '*') {
    sscanf (&s[pos], "%d", &height);
  }
  while (s[pos] != '-') pos ++;
  pos ++;

/* point height (use if no pixel height) */
  if (height==-1) {
    if (s[pos] != '-' && s[pos] != '*') {
      sscanf (&s[pos], "%d", &height);
      height /= 10;
    }
  }

/* use default if no height defined */
  if (height==-1) height= 0;

  mavlib_fonts[i]= CreateFont (height, 0, 0, 0, weight, italic, FALSE, FALSE,
	ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
	DEFAULT_PITCH|family, typeface);
  
  return 0;
}

/* current colour */
int mavlib_col_r= 0;
int mavlib_col_g= 0;
int mavlib_col_b= 0;

/* Routine to display a string in a 2D raster font */ 

void mav_gfxWindowStringDisplay(char *s, int font)
{
/* store text info for display later in WindowBuffersSwap */
/* position */
  mavlib_string_xs[mavlib_string_count]= mavlib_2d_x;
  mavlib_string_ys[mavlib_string_count]= mavlib_2d_y;

/* text */
  sprintf (mavlib_string_texts[mavlib_string_count], "%s", s);

/* colour */
  mavlib_string_colours[mavlib_string_count]= RGB (
		mavlib_col_r, mavlib_col_g, mavlib_col_b);

/* font */
  mavlib_string_fonts[mavlib_string_count]= font;

/* increment strings count */
  mavlib_string_count ++;
}

/* function to create a front buffer surface */
void mavlib_createFB (void)
{
  DDSURFACEDESC2 ddsd;

/* set the surface requirements */
  ZeroMemory (&ddsd, sizeof (ddsd));
  ddsd.dwSize= sizeof (ddsd);
  ddsd.dwFlags= DDSD_CAPS;
  ddsd.ddsCaps.dwCaps= DDSCAPS_PRIMARYSURFACE;

  if (mavlib_gfx_fullscreen) {
/* set to create back buffer automatically */
    ddsd.dwFlags= DDSD_CAPS|DDSD_BACKBUFFERCOUNT;
    ddsd.ddsCaps.dwCaps= DDSCAPS_COMPLEX | DDSCAPS_3DDEVICE |
		DDSCAPS_FLIP | DDSCAPS_PRIMARYSURFACE;
    ddsd.dwBackBufferCount= 1;

/* antialiasing (set this in back buffer for windowed rendering) */
    if (mavlib_gfx_aa)
      ddsd.ddsCaps.dwCaps2 |= DDSCAPS2_HINTANTIALIASING;
  }

/* choose system or video memory for surface */
  if (mavlib_gfx_software) {
    ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
  } else {
    ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
  }

/* create the surface */
  MAVD3DERR (mavlib_DD->lpVtbl->CreateSurface (mavlib_DD, &ddsd,
		&mavlib_D3DSurface, NULL),
		"failed to create buffers");
}

/* function to create a back buffer */
void mavlib_createBB (HWND hwnd, int i)
{
  DDSURFACEDESC2 ddsd;

  GetClientRect (hwnd, &(mavlib_winhand[i].viewRect));

  if (mavlib_gfx_fullscreen) {
/* buffer already created, just need to retrieve it's handle */
    ZeroMemory (&ddsd, sizeof (ddsd));
    ddsd.ddsCaps.dwCaps= DDSCAPS_BACKBUFFER;
    MAVD3DERR (mavlib_D3DSurface->lpVtbl->GetAttachedSurface (mavlib_D3DSurface,
		&ddsd.ddsCaps, &mavlib_winhand[i].bb),
		"failed to get back buffer");
  } else {
/* need to create a new surface for the back buffer */
    ZeroMemory (&ddsd, sizeof (ddsd));
    ddsd.dwSize= sizeof (ddsd);
    ddsd.dwFlags= DDSD_WIDTH|DDSD_HEIGHT|DDSD_CAPS;
    ddsd.ddsCaps.dwCaps= DDSCAPS_3DDEVICE|DDSCAPS_OFFSCREENPLAIN;

    if (mavlib_gfx_software) {
      ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
    } else {
      ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
    }

/* get window size */
    ddsd.dwWidth= mavlib_winhand[i].viewRect.right -
			mavlib_winhand[i].viewRect.left;
    ddsd.dwHeight= mavlib_winhand[i].viewRect.bottom -
			mavlib_winhand[i].viewRect.top;

/* get window rectangle in screen coords for buffer swap blits */
    GetClientRect (hwnd, &(mavlib_winhand[i].screenRect));
    ClientToScreen (hwnd, (POINT *) &(mavlib_winhand[i].screenRect.left));
    ClientToScreen (hwnd, (POINT *) &(mavlib_winhand[i].screenRect.right));

/* antialiasing */
    if (mavlib_gfx_aa)
      ddsd.ddsCaps.dwCaps2 |= DDSCAPS2_HINTANTIALIASING;

/* create the surface */
    MAVD3DERR (mavlib_DD->lpVtbl->CreateSurface (mavlib_DD, &ddsd,
		&mavlib_winhand[i].bb, NULL),
		"failed to create back buffer");
  }
}

/* flags passed to mav_gfxWindowOpen */
int mavlib_sb= 0; /* single buffer */
int mavlib_ab= 0; /* accum buffer */

/* function to get the mouse position associated with an event */
void mavlib_msgPointerGet (int win, int *x, int *y, int *rx, int *ry)
{
  DWORD mouse_pos;
  POINT point;

/* get the mouse position at the time of the last message */
  mouse_pos= GetMessagePos ();
  point.x= LOWORD(mouse_pos);
  point.y= HIWORD(mouse_pos);

  *rx= point.x;
  *ry= point.y;

  /* Get in coords of specified window */
  if (ScreenToClient(mavlib_winhand[win].win, &point)==0) {
    fprintf (stderr, "Error: failed to convert cursor pos\n");
    exit (1);
  }

  /* Store window cords */
  *x= point.x;
  *y= point.y;
}

/* callback function for Z buffer format enumeration */
static HRESULT WINAPI mavlib_enumZBufferCallback(DDPIXELFORMAT *ddpf, VOID *wanted)
{
  if (ddpf->dwFlags==DDPF_ZBUFFER) {
    memcpy (wanted, ddpf, sizeof (DDPIXELFORMAT));
    return D3DENUMRET_CANCEL;
  }

  return D3DENUMRET_OK;
}

/* function to create a Z buffer */
void mavlib_createZB (int i)
{
  DDPIXELFORMAT ppfzb;
  DDSURFACEDESC2 ddsd;

/* get Z buffer and attach to back buffer */
  MAVD3DERR (mavlib_D3D->lpVtbl->EnumZBufferFormats (mavlib_D3D,
		mavlib_pd3dguid, mavlib_enumZBufferCallback,
		(VOID *) &ppfzb),
		"failed Z buffer enumerate");

  if (ppfzb.dwSize != sizeof (DDPIXELFORMAT)) {
    fprintf (stderr, "Error: couldn't find a Z buffer format\n");
    exit (1);
  }

/* set surface requirements */
  ZeroMemory (&ddsd, sizeof (ddsd));
  ddsd.dwSize= sizeof (ddsd);
  ddsd.dwFlags= DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT|DDSD_PIXELFORMAT;
  if (mavlib_gfx_fullscreen) {
    ddsd.dwWidth= mavlib_config_w;
    ddsd.dwHeight= mavlib_config_h;
  } else {
    ddsd.dwWidth= mavlib_winhand[i].viewRect.right -
			mavlib_winhand[i].viewRect.left;
    ddsd.dwHeight= mavlib_winhand[i].viewRect.bottom -
			mavlib_winhand[i].viewRect.top;
  }

  memcpy (&ddsd.ddpfPixelFormat, &ppfzb, sizeof (DDPIXELFORMAT));
  ddsd.ddsCaps.dwCaps= DDSCAPS_ZBUFFER;

  if (mavlib_gfx_software) {
    ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
  } else {
    ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
  }

/* create the surface */
  MAVD3DERR (mavlib_DD->lpVtbl->CreateSurface (mavlib_DD, &ddsd,
		&mavlib_winhand[i].zb, NULL),
		"failed to create Z buffer");

/* attach to the back buffer */
  MAVD3DERR (mavlib_winhand[i].bb->lpVtbl->AddAttachedSurface (
		mavlib_winhand[i].bb, mavlib_winhand[i].zb),
		"failed to attach Z buffer");
}

/* function to get a Direct3D device handle */
void mavlib_createD3DDevice (void)
{
  int got_one= 0;
/* get D3D device */
/* if mavlib_pd3dguid is NULL then the user selected device wasn't found */
  if (mavlib_pd3dguid) {
    if (mavlib_D3D->lpVtbl->CreateDevice (mavlib_D3D, mavlib_pd3dguid,
		mavlib_backBuffer, &mavlib_D3DDevice) != D3D_OK) {
      fprintf (stderr, "Couldn't open selected GUID, trying defaults\n");
    } else {
      got_one= 1;
    }
  }

/* try other GUIDs */
  if (!got_one) {
    if (mavlib_D3D->lpVtbl->CreateDevice (mavlib_D3D,
		&IID_IDirect3DTnLHalDevice,
		mavlib_backBuffer, &mavlib_D3DDevice) != D3D_OK) {
      fprintf (stderr, "Couldn't open TnL hardware device, trying HAL\n");
      if (mavlib_D3D->lpVtbl->CreateDevice (mavlib_D3D,
		&IID_IDirect3DHALDevice,
		mavlib_backBuffer, &mavlib_D3DDevice) != D3D_OK) {
        fprintf (stderr,
		"Couldn't open HAL hardware device, trying software\n");
        if (mavlib_D3D->lpVtbl->CreateDevice (mavlib_D3D,
		&IID_IDirect3DRGBDevice, mavlib_backBuffer, &mavlib_D3DDevice)
		!= D3D_OK) {
	  fprintf (stderr, "Error: failed to create software device\n");
	  exit (1);
	} else {
	  mavlib_pd3dguid= &IID_IDirect3DRGBDevice;
	}
      } else {
	mavlib_pd3dguid= &IID_IDirect3DHALDevice;
      }
    } else {
      mavlib_pd3dguid= &IID_IDirect3DTnLHalDevice;
    }
  }

/* activate the depth buffer */
  MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderState (mavlib_D3DDevice,
		D3DRENDERSTATE_ZENABLE, TRUE),
		"failed to enable Z buffer");

  MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderState (mavlib_D3DDevice,
		D3DRENDERSTATE_ZWRITEENABLE, TRUE),
		"failed to enable Z writes");

/* set viewport */
  mavlib_setView (1);

/* reset cull mode */
  MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderState (mavlib_D3DDevice,
	D3DRENDERSTATE_CULLMODE, D3DCULL_NONE),
	"failed to set cull state");

/* set antialiasing */
  if (mavlib_gfx_aa) {
    MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderState (mavlib_D3DDevice,
	D3DRENDERSTATE_ANTIALIAS, D3DANTIALIAS_SORTINDEPENDENT),
	"failed to set AA");
  }
}

/* callback for texture format enumeration */
static HRESULT WINAPI mavlib_enumTexCallback (DDPIXELFORMAT *pf, VOID *this)
{
/* check for RGB only surfaces */
  if (!(pf->dwFlags & (DDPF_LUMINANCE|DDPF_BUMPLUMINANCE|DDPF_BUMPDUDV|
	DDPF_ALPHAPIXELS)) && (pf->dwFourCC==0) &&
	(pf->dwRGBBitCount>mavlib_texRGB.dwRGBBitCount) &&
	(pf->dwRGBBitCount>=16) &&
	(pf->dwRGBBitCount<=mavlib_gfx_textureBits)) {
    memcpy (&mavlib_texRGB, pf, sizeof (DDPIXELFORMAT));
    return D3DENUMRET_OK;
  }

/* check for RGB and single bit alpha */
  if (!(pf->dwFlags & (DDPF_LUMINANCE|DDPF_BUMPLUMINANCE|DDPF_BUMPDUDV)) &&
	(pf->dwFlags & DDPF_ALPHAPIXELS) && (pf->dwFourCC==0) &&
	(pf->dwRGBAlphaBitMask== (pf->dwRGBAlphaBitMask & 0x8001)) &&
	(pf->dwRGBBitCount>mavlib_tex1A.dwRGBBitCount) &&
	(pf->dwRGBBitCount>=16) &&
	(pf->dwRGBBitCount<=mavlib_gfx_textureBits)) {
    memcpy (&mavlib_tex1A, pf, sizeof (DDPIXELFORMAT));
    return D3DENUMRET_OK;
  }

/* check for RGBA surfaces */
  if (!(pf->dwFlags & (DDPF_LUMINANCE|DDPF_BUMPLUMINANCE|DDPF_BUMPDUDV)) &&
	(pf->dwFlags & DDPF_ALPHAPIXELS) && (pf->dwFourCC==0) &&
	!(pf->dwRGBAlphaBitMask== (pf->dwRGBAlphaBitMask & 0x8001)) &&
	(pf->dwRGBBitCount>mavlib_texRGBA.dwRGBBitCount) &&
	(pf->dwRGBBitCount>=16) &&
	(pf->dwRGBBitCount<=mavlib_gfx_textureBits)) {
    memcpy (&mavlib_texRGBA, pf, sizeof (DDPIXELFORMAT));
    return D3DENUMRET_OK;
  }

  return D3DENUMRET_OK;
}

/* This callback executed by DispatchMessage in mav_gfxWindowEventGet */

LONG WINAPI mavlib_winEventHandler(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam) 
{
  LONG rv= 1;
  int v1=-1, v2=-1;
  LPMINMAXINFO mmi;
  char ch;

  /* Set event indicator to zero - not a managed event */
  mavlib_win32EventGood=0;

  switch (umsg) {
  case WM_GETMINMAXINFO:
    mmi= (LPMINMAXINFO) lparam;
    mmi->ptMinTrackSize.y += MAVLIB_MIN_Y;
    return 0;
  case WM_MOVE:
    v1= mavlib_winlookup (hwnd);
/* update screen rectangle for buffer swap blits */
    GetClientRect (hwnd, &(mavlib_winhand[v1].screenRect));
    ClientToScreen (hwnd, (POINT *) &(mavlib_winhand[v1].screenRect.left));
    ClientToScreen (hwnd, (POINT *) &(mavlib_winhand[v1].screenRect.right));
    break;
  case WM_CREATE: /* Create Window event */
    if (mavlib_win32Create!=-1) 
    {
      mavlib_ddfunctype ddcreatefunc;

      mavlib_currwin= mavlib_win32Create;
      mavlib_wincount ++;
      mavlib_winhand[mavlib_win32Create].hdc= GetDC(hwnd);

/* check if first window */
      if (mavlib_first_create) {
/* get DD7 interface */

/* load the ddraw library (if this fails, DX probably isn't installed) */
        mavlib_ddrawDLL= LoadLibrary ("DDRAW.DLL");
        if (!mavlib_ddrawDLL) {
          fprintf (stderr, "Error: Load library (ddraw.dll) failed\n");
	  exit (1);
        }

/* get the create function (if this fails, probably not DX7) */
        ddcreatefunc= (mavlib_ddfunctype) GetProcAddress (mavlib_ddrawDLL,
		"DirectDrawCreateEx");

        if (!ddcreatefunc) {
          fprintf (stderr, "Error: Get function (DirectDrawCreateEx) failed\n");
	  exit (1);
        }

/* create the ddraw7 interface */
        MAVD3DERR ((*ddcreatefunc) (mavlib_pddguid, (VOID **) &mavlib_DD,
		&IID_IDirectDraw7, NULL),
		"failed to get DD7 interface");
      }

/* set cooperative level */
      if (mavlib_gfx_fullscreen) {
	MAVD3DERR (mavlib_DD->lpVtbl->SetCooperativeLevel (mavlib_DD, hwnd,
		DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT),
		"failed to set cooperative level");
      } else {
        MAVD3DERR (mavlib_DD->lpVtbl->SetCooperativeLevel (mavlib_DD, hwnd, 
		DDSCL_NORMAL),
		"failed to set cooperative level");
      }

/* set full screen display mode */
      if (mavlib_gfx_fullscreen) {
	MAVD3DERR (mavlib_DD->lpVtbl->SetDisplayMode (mavlib_DD,
		mavlib_config_w, mavlib_config_h, mavlib_config_d, 0, 0),
		"failed to set display mode");
      }

      if (mavlib_win32Create==1) {
/* get display surfaces */
        mavlib_createFB ();
      }

/* get back buffer */
      mavlib_createBB (hwnd, mavlib_win32Create);

      mavlib_backBuffer= mavlib_winhand[mavlib_win32Create].bb;

/* get clipper */
#if MAVLIB_CLIPPER
      if (!mavlib_gfx_fullscreen)
        mavlib_createClipper (hwnd);
#endif

      if (mavlib_first_create) {
/* get D3D interface */
        MAVD3DERR (mavlib_DD->lpVtbl->QueryInterface (mavlib_DD,
		&IID_IDirect3D7, (LPVOID *) &mavlib_D3D),
		"failed to get D3D7 interface");
      }

/* get Z buffer */
      mavlib_createZB (mavlib_win32Create);

      mavlib_ZBuffer= mavlib_winhand[mavlib_win32Create].zb;

      if (mavlib_first_create) {
/* get D3D device */
        mavlib_createD3DDevice ();

/* enumerate texture formats */
        ZeroMemory (&mavlib_texRGB, sizeof (DDPIXELFORMAT));
        ZeroMemory (&mavlib_tex1A, sizeof (DDPIXELFORMAT));
        ZeroMemory (&mavlib_texRGBA, sizeof (DDPIXELFORMAT));
        MAVD3DERR (mavlib_D3DDevice->lpVtbl->EnumTextureFormats (
		mavlib_D3DDevice, mavlib_enumTexCallback, NULL),
		"failed to enumerate texture formats");

        if (mavlib_texRGB.dwSize != sizeof (DDPIXELFORMAT)) {
	  fprintf (stderr, "Error: failed to get a texture pixel format\n");
          exit (1);
	}

/* make sure alpha texture pixel formats are valid */
        if (mavlib_tex1A.dwSize != sizeof (DDPIXELFORMAT)) {
	  memcpy (&mavlib_tex1A, &mavlib_texRGB, sizeof (DDPIXELFORMAT));
        }

        if (mavlib_texRGBA.dwSize != sizeof (DDPIXELFORMAT)) {
	  memcpy (&mavlib_texRGBA, &mavlib_tex1A, sizeof (DDPIXELFORMAT));
        }

/* set RGBA to 1 bit alpha if required */
	if (mavlib_config_1a && (mavlib_gfx_textureBits == 16)) {
	  memcpy (&mavlib_texRGBA, &mavlib_tex1A, sizeof (DDPIXELFORMAT));
	}
      }

      mavlib_first_create= 0;
    }
    else
    {
      fprintf (stderr, "Error: unexpected create message\n");
      exit (1);
    }
    break;
  case WM_CLOSE: /* Pressed on close icon */
    exit(1);
    break;
  case WM_SYSKEYDOWN: /* Keyboard event - fill in info */
  case WM_KEYDOWN:
    if (v1==-1) v1=0;
  case WM_SYSKEYUP:
  case WM_KEYUP:
    if (v1==-1) v1=1;

    /* Bit 30 of lparam is set if key already held down */
    if (v1==0 && lparam & (1 << 30)) break;

    /* The return value of this event */
    mavlib_win32EventGood=1; 

    /* Get the id of the window in which the event occurred */
    mavlib_win32EventInfo[0]= mavlib_winlookup(hwnd);

    /* Get pointer position at time of message */
    mavlib_msgPointerGet(mavlib_win32EventInfo[0], &mavlib_win32EventInfo[1], &mavlib_win32EventInfo[2], &mavlib_win32EventInfo[3], &mavlib_win32EventInfo[4]);
 

    /* Pressed or released */
    mavlib_win32EventInfo[5]=v1;

    /* Get modifier status */
    if (GetKeyState(VK_SHIFT)<0) /* Shift key up/down */ 
    {
      mavlib_win32EventInfo[7]= 1; /* Pressed (which is represented by 0 elsewhere!) */
    }
    else
    {
      mavlib_win32EventInfo[7]= 0; /* Released */
    }

    if (GetKeyState(VK_CONTROL)<0) /* Ctrl key up/down */ 
    {
      mavlib_win32EventInfo[8]= 1;
    }
    else
    {
      mavlib_win32EventInfo[8]= 0;
    }
    
    if (GetKeyState(VK_MENU)<0) /* Alt key up/down */ 
    {
      mavlib_win32EventInfo[9]= 1;
    }
    else
    {
      mavlib_win32EventInfo[9]= 0;
    }

    /* Translate keycode into ASCII value or #defines */
    mavlib_win32EventInfo[6]=0;
    switch (wparam) {
    case VK_F1: mavlib_win32EventInfo[6]= 300; break;
    case VK_F2: mavlib_win32EventInfo[6]= 301; break;
    case VK_F3: mavlib_win32EventInfo[6]= 302; break;
    case VK_F4: mavlib_win32EventInfo[6]= 303; break;
    case VK_F5: mavlib_win32EventInfo[6]= 304; break;
    case VK_F6: mavlib_win32EventInfo[6]= 305; break;
    case VK_F7: mavlib_win32EventInfo[6]= 306; break;
    case VK_F8: mavlib_win32EventInfo[6]= 307; break;
    case VK_F9: mavlib_win32EventInfo[6]= 308; break;
    case VK_F10: mavlib_win32EventInfo[6]= 309; break;
    case VK_F11: mavlib_win32EventInfo[6]= 310; break;
    case VK_F12: mavlib_win32EventInfo[6]= 311; break;
    case VK_UP: mavlib_win32EventInfo[6]= 312; break;
    case VK_DOWN: mavlib_win32EventInfo[6]= 313; break;
    case VK_LEFT: mavlib_win32EventInfo[6]= 314; break;
    case VK_RIGHT: mavlib_win32EventInfo[6]= 315; break;
    case VK_PRIOR: mavlib_win32EventInfo[6]= 316; break;
    case VK_NEXT: mavlib_win32EventInfo[6]= 317; break;
    case VK_SHIFT: ch= lparam >> 16;
		   if (ch==MapVirtualKey (VK_SHIFT, 0))
		     mavlib_win32EventInfo[6]= 318; /* left shift */
		   else
		     mavlib_win32EventInfo[6]= 319; /* right shift */
		   break;
    case VK_MENU: if (lparam & (1<<24))
		    mavlib_win32EventInfo[6]= 321; /* right alt */
		  else
		    mavlib_win32EventInfo[6]= 320; /* left alt */
		  break;
    case VK_HOME: mavlib_win32EventInfo[6]= 324; break;
    case VK_END: mavlib_win32EventInfo[6]= 325; break;
    case VK_INSERT: mavlib_win32EventInfo[6]= 326; break;
    case VK_CONTROL: if (lparam & (1<<24))
		       mavlib_win32EventInfo[6]= 328; /* right ctrl */
		     else
		       mavlib_win32EventInfo[6]= 327; /* left ctrl */
		     break;
    case VK_CAPITAL: mavlib_win32EventInfo[6]= 329; break;
    default: mavlib_win32EventInfo[6]= MapVirtualKey(wparam, 2); break;
    }

    /* Windows reports everything as uppercase - compensate for this */
    if (mavlib_win32EventInfo[6]>='A' && mavlib_win32EventInfo[6]<='Z' && !mavlib_win32EventInfo[7]) mavlib_win32EventInfo[6]+=32;

    /* check for shift key and change punctuation characters */
/* £ and ¬ don't work */
    if (mavlib_win32EventInfo[7]) {
      switch (mavlib_win32EventInfo[6]) {
	case '`': mavlib_win32EventInfo[6]= '¬'; break;
        case '1': mavlib_win32EventInfo[6]= '!'; break;
        case '2': mavlib_win32EventInfo[6]= '\"'; break;
        case '3': mavlib_win32EventInfo[6]= '£'; break;
        case '4': mavlib_win32EventInfo[6]= '$'; break;
        case '5': mavlib_win32EventInfo[6]= '%'; break;
        case '6': mavlib_win32EventInfo[6]= '^'; break;
        case '7': mavlib_win32EventInfo[6]= '&'; break;
        case '8': mavlib_win32EventInfo[6]= '*'; break;
        case '9': mavlib_win32EventInfo[6]= '('; break;
        case '0': mavlib_win32EventInfo[6]= ')'; break;
        case '-': mavlib_win32EventInfo[6]= '_'; break;
        case '=': mavlib_win32EventInfo[6]= '+'; break;
        case '\\': mavlib_win32EventInfo[6]= '|'; break;
        case '[': mavlib_win32EventInfo[6]= '{'; break;
        case ']': mavlib_win32EventInfo[6]= '}'; break;
        case ';': mavlib_win32EventInfo[6]= ':'; break;
        case '\'': mavlib_win32EventInfo[6]= '@'; break;
        case '#': mavlib_win32EventInfo[6]= '~'; break;
        case ',': mavlib_win32EventInfo[6]= '<'; break;
        case '.': mavlib_win32EventInfo[6]= '>'; break;
        case '/': mavlib_win32EventInfo[6]= '?'; break;
	default: /* do nothing */;
      }
    }

    /* No event if we cant translate keycode */
    if (mavlib_win32EventInfo[6]==0) mavlib_win32EventGood=0;

    /* End of event */
    mavlib_win32EventInfo[10]= -999;
    break;

  case WM_LBUTTONDOWN: /* Mouse button event - fill in info */
    if (v1==-1) v1=1;
    if (v2==-1) v2=0;
  case WM_MBUTTONDOWN:
    if (v1==-1) v1=2;
    if (v2==-1) v2=0;
  case WM_RBUTTONDOWN:
    if (v1==-1) v1=3;
    if (v2==-1) v2=0;
  case WM_LBUTTONUP:
    if (v1==-1) v1=1;
    if (v2==-1) v2=1;
  case WM_MBUTTONUP:
    if (v1==-1) v1=2;
    if (v2==-1) v2=1;
  case WM_RBUTTONUP:
    if (v1==-1) v1=3;
    if (v2==-1) v2=1;

/* catch the mouse to trap button up events */
    if (v2==0) { /* button pressed */
/* catch the mouse */
      if (!mavlib_win32MouseCaught) SetCapture (hwnd); 
      mavlib_win32MouseWin[v1]= hwnd; /* store the window for this event */
      mavlib_win32MouseCaught ++; /* keep count of number of captures */
    } else {
/* release the mouse */
      mavlib_win32MouseCaught --;
      mavlib_win32MouseWin[v1]= NULL;
      if (!mavlib_win32MouseCaught) {
        ReleaseCapture ();
      }
    }

    /* The return value of this event */
    mavlib_win32EventGood=2;

    /* Get the id of the window in which the event occurred */
    mavlib_win32EventInfo[0]= mavlib_winlookup(hwnd);

    /* Get pointer position */
    mavlib_msgPointerGet(mavlib_win32EventInfo[0], &mavlib_win32EventInfo[1], &mavlib_win32EventInfo[2], &mavlib_win32EventInfo[3], &mavlib_win32EventInfo[4]);


    /* Pressed or released */
    mavlib_win32EventInfo[5]=v2;

    /* Which button */
    mavlib_win32EventInfo[6]=v1;

    /* Get modifier status */
    if (GetKeyState(VK_SHIFT)<0) /* Shift key up/down */ 
    {
      mavlib_win32EventInfo[7]= 1; /* Pressed (which is represented by 0 elsewhere!) */
    }
    else
    {
      mavlib_win32EventInfo[7]= 0; /* Released */
    }

    if (GetKeyState(VK_CONTROL)<0) /* Ctrl key up/down */ 
    {
      mavlib_win32EventInfo[8]= 1;
    }
    else
    {
      mavlib_win32EventInfo[8]= 0;
    }
    
    if (GetKeyState(VK_MENU)<0) /* Alt key up/down */ 
    {
      mavlib_win32EventInfo[9]= 1;
    }
    else
    {
      mavlib_win32EventInfo[9]= 0;
    }
    
    /* End of event */
    mavlib_win32EventInfo[10]=-999;
    break;

  case WM_SIZE:  /* Resize event - store in winhand for now */
    v1= mavlib_winlookup(hwnd);
    if (wparam==SIZE_MINIMIZED) {
      mavlib_winhand[v1].mapped= 2; /* unmap (+1) */
    } else {
      mavlib_winhand[v1].width= LOWORD(lparam);
      mavlib_winhand[v1].height= HIWORD(lparam);
      mavlib_winhand[v1].resized= 1;
      if (!mavlib_gfx_fullscreen) {
/* deal with attached buffers */
        MAVD3DERR (mavlib_winhand[v1].bb->lpVtbl->DeleteAttachedSurface (
		mavlib_winhand[v1].bb, 0, mavlib_winhand[v1].zb),
		"failed to delete attached surfaces");

	mavlib_winhand[v1].bb->lpVtbl->Release (mavlib_winhand[v1].bb);
	mavlib_winhand[v1].zb->lpVtbl->Release (mavlib_winhand[v1].zb);

	mavlib_createBB (hwnd, v1);

	mavlib_createZB (v1);

	if (mavlib_currwin==v1) {
	  int was_in_scene;

	  mavlib_backBuffer= mavlib_winhand[v1].bb;
	  mavlib_ZBuffer= mavlib_winhand[v1].zb;

	  was_in_scene= mavlib_gfx_inScene;
	  if (was_in_scene) mavlib_endScene ();
#if MAVLIB_CLIPPER
	  MAVD3DERR (mavlib_D3DClipper->lpVtbl->SetHWnd (mavlib_D3DClipper,
		0, hwnd),
		"failed to set clipper hwnd");
#endif
	  MAVD3DERR (mavlib_D3DDevice->lpVtbl->SetRenderTarget (
		mavlib_D3DDevice, mavlib_backBuffer, 0),
		"failed to reassign back buffer");
	  mavlib_setView (v1);

	  if (was_in_scene) mavlib_beginScene ();
	  mavlib_gfx_inScene= was_in_scene;
	}
      }
    }
    break;
  case WM_QUERYOPEN: /* sent before opening an iconic window */
    v1= mavlib_winlookup(hwnd);
    mavlib_winhand[v1].mapped= 1; /* map (+1) */
    break;
  case WM_PAINT: /* expose event */
/* WM not happy unless you validate the update region */
/* NULL validates the whole window */
    ValidateRect (hwnd, NULL);
    v1= mavlib_winlookup(hwnd);
/* store for checking in mav_gfxWindowEventGet */
    mavlib_winhand[v1].exposed= 1;
    break;
  default: /* Let the system deal with all other events */
    rv= DefWindowProc(hwnd, umsg, wparam, lparam);
  }

  return rv;
}



/* Routine to open a window */

void mav_gfxWindowOpen(int id, int x, int y, int width, int height, char *nm, char *disp, int wmplacement, int sb, int qb, int ms, int ab, int stenb, int desta, int *wret, int *hret)
{
  RECT wr;
  DWORD win_style;
  int old_h= height;
  int old_w= width;

  if (mavlib_gfx_fullscreen || mav_opt_fullscreen) {
    win_style= WS_POPUP;
    mavlib_gfx_fullscreen= 1;
    x= 0;
    y= 0;
    width= mavlib_config_w;
    height= mavlib_config_h;
/* suppress text output */
    mav_opt_output= 0;
  } else {
    win_style= WS_OVERLAPPEDWINDOW;

/* get original window size */
    wr.left= x;
    wr.right= width + x;
    wr.top= y;
    wr.bottom= height + y;

    if (AdjustWindowRect (&wr, WS_OVERLAPPEDWINDOW, FALSE)) {
      x= wr.left;
      y= wr.top;
      old_w= wr.right-x;
      old_h= wr.bottom-y;
    }
  }

  if (qb) {
    fprintf (stderr, "Error: Quad buffer visuals not supported on this platform\n");
    exit (1);
  }

  if (ms) {
    fprintf (stderr, "Error: Multisampled visuals not supported on this platform\n");
    exit (1);
  }

/* stencil buffers are supported - add later */
  if (stenb) {
    fprintf (stderr, "Error: Stencil buffer visuals not supported on this platform\n");
    exit (1);
  }

  if (desta) {
    fprintf (stderr, "Error: Destination alpha buffer visuals not supported on this platform\n");
    exit (1);
  }

/* assume we'll get the correct window size */
/* probably ought to check it (after the initial resize event) */
  *wret= width;
  *hret= height;

  if (!mavlib_gfx_fullscreen) {
/* need to create a window with the maximum size it can be resized to */
    width= GetSystemMetrics (SM_CXSCREEN);
    height= GetSystemMetrics (SM_CYSCREEN);
  }

/* get window size with borders so we get a client area of the desired size */
  wr.left= x;
  wr.right= width+x;
  wr.top= y;
  wr.bottom= height+y;

  if (AdjustWindowRect (&wr, win_style, FALSE)) {
    x= wr.left;
    y= wr.top;
    width= wr.right-x;
    height= wr.bottom-y;
  }

/* set values for WM_CREATE event */
  mavlib_win32Create=id;
  mavlib_sb= sb;
  mavlib_ab= ab;

  mavlib_winhand[id].win= CreateWindow("Maverik", nm,
			 win_style,
			 CW_USEDEFAULT, CW_USEDEFAULT, width, height,
                         NULL, NULL, mavlib_dpy, NULL);
  mavlib_win32Create=-1;

/* reposition and resize window to original placement */
  if (!mavlib_gfx_fullscreen)
    MoveWindow (mavlib_winhand[id].win, x, y, old_w, old_h, FALSE);

/* set event values */
  mavlib_winhand[id].width=-1;
  mavlib_winhand[id].height=-1;
  mavlib_winhand[id].resized=0;
  mavlib_winhand[id].exposed= 0;
  mavlib_winhand[id].mapped= 0;

  if (!mavlib_winhand[id].win) {
    fprintf (stderr, "Error: couldn't open window\n");
    exit (1);
  }

  ShowWindow(mavlib_winhand[id].win, SW_SHOW);
  SetFocus(mavlib_winhand[id].win);
/* at this point a resize event will have occurred but (hopefully) */
/* to the size that was originally chosen */

  /* Set to active window */
  mav_gfxWindowSet(id);
}



/* Routine to close a window */

void mav_gfxWindowClose(int id)
{
/* this function should never be called for the original (id==1) window */

/* release D3D handles for this window */
  MAVD3DERR (mavlib_winhand[id].bb->lpVtbl->DeleteAttachedSurface (
		mavlib_winhand[id].bb, 0, mavlib_winhand[id].zb),
		"failed to delete attached surfaces");

  mavlib_winhand[id].bb->lpVtbl->Release (mavlib_winhand[id].bb);
  mavlib_winhand[id].zb->lpVtbl->Release (mavlib_winhand[id].zb);

/* remove the window */
  DestroyWindow(mavlib_winhand[id].win);
  mavlib_winhand[id].win= (HWND) NULL;
}



/* 
   Check if any events are outstanding (do not block if there are not) 
   Return value gives the type of event.
*/

int mav_gfxWindowEventPeek(void)
{
  int rv=0;
  int winid=0;

  return (rv + (winid<<8));
}


int mav_gfxWindowEventGet(int *info)
{
  int rv=0;
  MSG msg;
  int i=0;

  /* Look for mapping, resize or expose events */
  for (i=0; i<MAX_WINS; i++) {
    if (mavlib_winhand[i].win && mavlib_winhand[i].mapped) {
      info[0]= i;
      info[1]= mavlib_winhand[i].mapped - 1;
      mavlib_winhand[i].mapped= 0;
      return (4 + (info[0]<<8)); /* map/unmap event */
    }
    if (mavlib_winhand[i].win && mavlib_winhand[i].resized) {
      info[0]= i;
      info[1]= mavlib_winhand[i].width;
      info[2]= mavlib_winhand[i].height;
      mavlib_winhand[i].resized=0;
      return (3 + (info[0]<<8)); /* Indicates a resize event */
    }
    if (mavlib_winhand[i].win && mavlib_winhand[i].exposed) {
      info[0]= i;
      mavlib_winhand[i].exposed= 0;
      return (6 + (info[0]<<8)); /* Indicates an expose event */
    }
  }

/* check for released mouse capture */
/* this section isn't really necessary unless there are other processes */
/* running which capture the mouse (or you press the windows key) */
  if (mavlib_win32MouseEventsPending || mavlib_win32MouseCaught) {
    if (!GetCapture ()) {
/* mouse no longer caught - simulate button up event for all pressed buttons */
      int i;
      int not_got_one= 1;

      mavlib_win32MouseCaught= 0;

      for (i=1; not_got_one && i<4; i++) {
	if (mavlib_win32MouseWin[i]) {
	  not_got_one= 0;
	  info[0]= mavlib_winlookup (mavlib_win32MouseWin[i]);
	  mav_gfxWindowPointerGet (info[0], &info[1], &info[2], &info[3],
			&info[4], NULL);
	  info[5]= 1; /* released */
	  info[6]= i; /* button */
/* get modifier status */
	  if (GetKeyState (VK_SHIFT)<0)
	    info[7]= 1;
	  else
	    info[7]= 0;

	  if (GetKeyState (VK_CONTROL)<0)
	    info[8]= 1;
	  else
	    info[8]= 0;

	  if (GetKeyState (VK_MENU)<0)
	    info[9]= 1;
	  else
	    info[9]= 0;

	  info[10]= -999;
/* reset mouse win */
	  mavlib_win32MouseWin[i]= NULL;
	}
      }

      if (not_got_one) mavlib_win32MouseEventsPending= 0;
      else {
	mavlib_win32MouseEventsPending= 1;
	return (2 + (info[0]<<8)); /* button event */
      }
    }
  }
/* end of mouse-capture-maybe-not-necessary section */


  /* Get next event - non blocking */
  if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {	

    /* Deal with event - this calls the mavlib_winEventHandler callback fn */
/* no point in calling TranslateMessage as Maverik interprets virtual keys */
/* and it just sends an extra (ignored) message to the queue */
    DispatchMessage(&msg);
      
    /* Is this an event dealt with by Maverik - value set by callback fn */
    if (mavlib_win32EventGood) {
      /* Copy values filled in by callback fn into info */
      i=0;
      while (mavlib_win32EventInfo[i]!=-999) {
	info[i]=mavlib_win32EventInfo[i];
	i++;
	rv=mavlib_win32EventGood;
      }
    }
  }

  return (rv + (info[0]<<8));
}



/* Routine to return the position of the mouse in a window and root coords */

int mav_gfxWindowPointerGet(int win, int *x, int *y, int *rx, int *ry, int *buts)
{
  int rv=1;
  POINT point;

  if (win>0 && win<MAX_WINS && mavlib_winhand[win].win) 
  {
    if (GetCursorPos(&point)==0) {
      fprintf (stderr, "Error: failed to get cursor pos\n");
      exit (1);
    }

    /* Store root cords */
    *rx= point.x;
    *ry= point.y;
  
    /* Get in coords of specified window */
    if (ScreenToClient(mavlib_winhand[win].win, &point)==0) {
      fprintf (stderr, "Error: failed to convert cursor pos\n");
      exit (1);
    }

    /* Store window cords */
    *x= point.x;
    *y= point.y;

/* get mouse button status */
    if (buts) {
      if (GetKeyState (VK_LBUTTON)<0) buts[0]= 0;
      else buts[0]= 1;
      if (GetKeyState (VK_MBUTTON)<0) buts[1]= 0;
      else buts[1]= 1;
      if (GetKeyState (VK_RBUTTON)<0) buts[2]= 0;
      else buts[2]= 1;
     }
  }
  else
  {
    rv=0;
  }

  return rv;
}



/* Routine to set the mouses position */

void mav_gfxWindowPointerSet(int win, int x, int y)
{
  POINT point;
  
  /* Point in coords of specified window */
  point.x= x;
  point.y= y;

  /* Get in coords of root window */
  if (ClientToScreen(mavlib_winhand[win].win, &point)==0) {
    fprintf (stderr, "Error: failed to convert cursor pos\n");
    exit (1);
  }

  /* Set cursor pos */
  SetCursorPos(point.x, point.y);
}



/* Routine to return the state of a key */

int mav_gfxWindowKeyGet(int key)
{
  int vk;

  /* Convert to keycode */
  switch (key) {
  case 300: vk= VK_F1; break;
  case 301: vk= VK_F2; break;
  case 302: vk= VK_F3; break;
  case 303: vk= VK_F4; break;
  case 304: vk= VK_F5; break;
  case 305: vk= VK_F6; break;
  case 306: vk= VK_F7; break;
  case 307: vk= VK_F8; break;
  case 308: vk= VK_F9; break;
  case 309: vk= VK_F10; break;
  case 310: vk= VK_F11; break;
  case 311: vk= VK_F12; break;
  case 312: vk= VK_UP; break;
  case 313: vk= VK_DOWN; break;
  case 314: vk= VK_LEFT; break;
  case 315: vk= VK_RIGHT; break;
  case 316: vk= VK_PRIOR; break;
  case 317: vk= VK_NEXT; break;
  case 318: vk= VK_SHIFT; break;
  case 319: vk= VK_SHIFT; break;
  case 320: vk= VK_MENU; break;
  case 321: vk= VK_MENU; break;
  case 324: vk= VK_HOME; break;
  case 325: vk= VK_END; break;
  case 326: vk= VK_INSERT; break;
  case 327: vk= VK_CONTROL; break;
  case 328: vk= VK_CONTROL; break;
  case 329: vk= VK_CAPITAL; break;
  default: vk= key; break;
  }

  /* Get key state */
  if (GetKeyState(vk)<0)
  {
    return 0; /* Pressed */
  }
  else
  {
    return 1; /* Released */
  }
}



/* Routines specific to Voodoo */

void mav_gfx3DfxModeSet(int fullscreen)
{
}

int mav_gfx3DfxBoardSet(int bd)
{
  int rv= 0;

  return rv;
}
