/* This file actually implements three drivers (win32 and DirectX 
   windowed/fullscreen) drivers, because they have a lot of common stuff. */
#include <config.h>
#ifdef WIN32_DRIVER
#include <windows.h>
#ifdef DDRAW_DRIVER
#include <ddraw.h>
#endif
#include <malloc.h>
#include <string.h>
#include <stdio.h>
#include <xmenu.h>
#include <xerror.h>
#include "ui.h"
#include "ui_win32.h"
#include "resource.h"
#include "xio.h"
#include <cursor.h>

#define MAXRESOLUTIONS 256
#define DXWIDTH 320
#define DXHEIGHT 200
#define DXBPP 8
#define WWIDTH (480+4*48)
#define WHEIGHT (360+4*36)

#define DXFULLSCREEN 1
#define DXWINDOWED   2

#define MOUSEWIDTH 16
#define MOUSEHEIGHT 16

#ifdef HAVE_GETTEXT

#include <libintl.h>

#include <locale.h>

#else

#define gettext(STRING) STRING

#endif



HINSTANCE hInstance;
HWND hWnd;
CONST char *helptopic = "main";

struct ui_driver win32_driver;
struct ui_driver dxw_driver, dxf_driver;

static int initialized = 0, needredraw = 0;

static char *helpname = NULL;

/* This variables are used by drivers in initializetion */
static int windowpos;
static CONST char *size = "520x430";
static CONST char *dxsize = "320x200x8";


/* Display information */
static int displayX, displayY, bitDepth;
static int lineSize;
static int fontWidth, fontHeight;
static HPALETTE hPalette = NULL;
static char *buffer1;
static char *buffer2;
static int currentbuff = 0;
static BITMAPINFO *bmp = NULL;

/* Mouse */
static int mouseX, mouseY, mouseButtons = 0;
static int captured = 0, tmpcaptured = 0;

/* Keyboard state */
static int altPressed;
static int arrowsPressed;

/* WindowProc comunication variables */
static BOOL closeFlag;
static int resized = 0;
static int active = 1;

/* Curent class information */
static HICON hIcon;
static HICON hIconSm;
static HFONT hFont = NULL;

/* DirectX stuff */
#ifdef DDRAW_DRIVER
int directX = 0;
static LPDIRECTDRAWPALETTE dxPalette = NULL;
static LPDIRECTDRAW lpDD = NULL;
static LPDIRECTDRAW2 lpDD2 = NULL;
static LPDIRECTDRAWSURFACE lpSurfaces[2], BackSurface[2];
static DDSURFACEDESC surface[2];
static CONST char *mousepointer = mouse_pointer_data;
static char *storeddata = NULL;
static HMODULE hModule = NULL;
static int oldmouseX, oldmouseY;
#else
#define directX 0
#endif
static PUCHAR backpalette[256][4];
static HMODULE hModule2;
static RECT rcWindow;
static RECT rcViewport;
static RECT rcScreen;
static int MyHelpMsg;
/*clipboard*/
static WORD clipboard_format;

/* forward declarations */
#ifdef DDRAW_DRIVER
static void DeInitDD (void);
static void PaintDD (void);
static void UpdateMouseDD (void);
#endif
static void Paint (HDC hDC);
static void CalculateBITMAPINFO (void);
static void win32_display (void);
static void DeInitWindow (void);

#ifdef DDRAW_DRIVER
  /* FIXME: In windowed mode we don't support 8bpp yet! */
#define DXSUPPORTEDDEPTH(fullscreen,depth) \
  (!(depth < 8 || (!fullscreen && depth != 16 && depth !=24 && depth != 32)))

static char *
store (char *data, int depth, int lpitch, int width, int height, int xpos,
       int ypos)
{
  int d = depth / 8;
  char *store = malloc (d * MOUSEWIDTH * MOUSEHEIGHT);
  int y;
  if (xpos + MOUSEWIDTH > width)
    xpos = width - MOUSEWIDTH;
  if (ypos + MOUSEHEIGHT > height)
    ypos = height - MOUSEHEIGHT;
  if (xpos < 0)
    xpos = 0;
  if (ypos < 0)
    ypos = 0;
  for (y = 0; y < MOUSEHEIGHT; y++)
    memcpy (store + d * MOUSEWIDTH * y, data + xpos * d + (ypos + y) * lpitch,
	    MOUSEWIDTH * d);
  return store;
}
static void
restore (char *data, CONST char *store, int depth, int lpitch, int width,
	 int height, int xpos, int ypos)
{
  int y;
  int d = depth / 8;
  if (xpos + MOUSEWIDTH > width)
    xpos = width - MOUSEWIDTH;
  if (ypos + MOUSEHEIGHT > height)
    ypos = height - MOUSEHEIGHT;
  if (xpos < 0)
    xpos = 0;
  if (ypos < 0)
    ypos = 0;
  for (y = 0; y < MOUSEHEIGHT; y++)
    memcpy (data + xpos * d + (ypos + y) * lpitch, store + d * MOUSEWIDTH * y,
	    MOUSEWIDTH * d);
}
static void
drawmouse (char *data, CONST char *mouse, int depth, int lpitch, int width,
	   int height, int xpos, int ypos)
{
  int x, y, z, c;
  int d = depth / 8;
  for (y = 0; y < MOUSEWIDTH; y++)
    for (x = 0; x < MOUSEWIDTH; x++)
      if (mouse[x + MOUSEWIDTH * y] && x + xpos > 0 && (x + xpos) < width
	  && y + ypos > 0 && y + ypos < height)
	{
	  c = mouse[x + MOUSEWIDTH * y] == 2 ? (d == 1 ? 1 : 255) : 0;
	  for (z = 0; z < d; z++)
	    data[z + d * (x + xpos) + (y + ypos) * lpitch] = c;
	}
}
#endif
static void
getdimens (float *width, float *height)
{
  HDC hDC = GetDC (hWnd);
  *width = GetDeviceCaps (hDC, HORZSIZE) / 10.0;
  *height = GetDeviceCaps (hDC, VERTSIZE) / 10.0;
  if (*width > 100 || *width < 1)
    *width = 29.0;
  if (*height > 100 || *height < 1)
    *height = 21.0;
  ReleaseDC (hWnd, hDC);
}
static void
getres (float *width, float *height)
{
  HDC hDC = GetDC (hWnd);
  *width = 2.54 / GetDeviceCaps (hDC, LOGPIXELSX);
  *height = 2.54 / GetDeviceCaps (hDC, LOGPIXELSY);
  ReleaseDC (hWnd, hDC);
}

/******************************************************************************
                             Win32 driver helper routines
 */

static LRESULT CALLBACK
WindowProc (HWND hWnd,		// handle to window
	    UINT uMsg,		// message identifier
	    WPARAM wParam,	// first message parameter
	    LPARAM lParam	// second message parameter
  )
{
  PAINTSTRUCT paintStruct;
  HDC hDC;
  if (uMsg == (unsigned int) MyHelpMsg)
    {
      win32_help (NULL, helptopic);
      return 0;
    }
  switch (uMsg)
    {
    case WM_COMMAND:
      win32_pressed (wParam);
      break;
    case WM_SIZE:
      // resize window
      if (directX == DXFULLSCREEN)
	return 0;
      if (LOWORD (lParam) == 0 && HIWORD (lParam) == 0)
	{
	  active = 0;
	  break;
	}			/*Minimized window */
      active = 1;
      if (displayX != LOWORD (lParam) || displayY != HIWORD (lParam))
	resized = 1;
      displayX = LOWORD (lParam);
      displayY = HIWORD (lParam);
      break;
    case WM_DISPLAYCHANGE:
      if (directX == DXFULLSCREEN)
	return 0;
      mouseButtons = 0;
      resized = 1;
      hDC = GetDC (hWnd);
      bitDepth = GetDeviceCaps (hDC, BITSPIXEL);
      ReleaseDC (hWnd, hDC);
      break;
    case WM_CLOSE:
      // close window
      closeFlag = TRUE;
      return 0;
    case WM_MOUSEMOVE:
    case WM_LBUTTONUP:
    case WM_LBUTTONDOWN:
    case WM_RBUTTONUP:
    case WM_RBUTTONDOWN:
    case WM_MBUTTONUP:
    case WM_MBUTTONDOWN:
      // handle mouse move and mouse buttons
      mouseButtons = wParam;
      if (!captured)
	{
	  if (mouseButtons && !tmpcaptured)
	    SetCapture (hWnd), tmpcaptured = 1;
	  if (!mouseButtons && tmpcaptured)
	    ReleaseCapture (), tmpcaptured = 0;
	}
      mouseX = (short) LOWORD (lParam);
      mouseY = (short) HIWORD (lParam);
#ifdef DDRAW_DRIVER
      if (directX == DXFULLSCREEN)
	{
	  POINT p;
	  GetCursorPos (&p);
	  mouseX = p.x;
	  mouseY = p.y;
	  UpdateMouseDD ();
	}
#endif
      break;
    case WM_PAINT:
      // redraw screen
      if (directX == DXFULLSCREEN)
	return 0;
      needredraw = 1;
      if (GetUpdateRect (hWnd, NULL, FALSE))
	{
	  HDC hDC = BeginPaint (hWnd, &paintStruct);
	  if (hDC)
	    {
#ifdef DDRAW_DRIVER
	      if (directX)
		PaintDD ();
	      else
#endif
		Paint (hDC);
	      EndPaint (hWnd, &paintStruct);
	    }
	}
      return 0;
    case WM_QUERYNEWPALETTE:
      // windows calls this when window is reactivated.
      if (directX == DXFULLSCREEN)
	return 0;
      hDC = GetDC (hWnd);
#ifdef DDRAW_DRIVER
      if (directX == DXWINDOWED)
	{
	  if (dxPalette)
	    {
	      IDirectDrawSurface_SetPalette (lpSurfaces[0], dxPalette);
	      IDirectDrawPalette_SetEntries (dxPalette, 0, 0, 255,
					     (PALETTEENTRY *) backpalette);
	    }
	}
      else
#endif
	{
	  SelectPalette (hDC, hPalette, FALSE);
	  RealizePalette (hDC);
	}
      ReleaseDC (hWnd, hDC);
      return TRUE;
    case WM_MOVE:
      if (directX != DXFULLSCREEN)
	{
	  GetWindowRect (hWnd, &rcWindow);
	  GetClientRect (hWnd, &rcViewport);
	  GetClientRect (hWnd, &rcScreen);
	  ClientToScreen (hWnd, (POINT *) & rcScreen.left);
	  ClientToScreen (hWnd, (POINT *) & rcScreen.right);
	}
      break;
    case WM_SETCURSOR:
      if (directX == DXFULLSCREEN)
	{
	  SetCursor (NULL);
	  return TRUE;
	}
      break;
#ifdef DDRAW_DRIVER
    case WM_ACTIVATEAPP:
      {
	int oldactive = active;
	mouseButtons = 0;
	if (directX == DXFULLSCREEN)
	  {
	    needredraw = 1;
	    active = (wParam == WA_ACTIVE)
	      || (wParam == WA_CLICKACTIVE) /*(BOOL) wParam */ ;
	    PaintDD ();
	    if (!oldactive && active && captured)
	      SetCursor (NULL), SetCapture (hWnd);
	    if (oldactive && !active && captured)
	      ReleaseCapture ();
	    return 0L;
	  }
      }
#endif
      break;
    }
  return DefWindowProc (hWnd, uMsg, wParam, lParam);
}

/*Create Xaos Window. It is either used for normal window mode or
   as basis for DirectX */
static int
InitWindow (void)
{
  int width = CW_USEDEFAULT, height = CW_USEDEFAULT;
  int xpos = CW_USEDEFAULT, ypos = CW_USEDEFAULT;
  LOGPALETTE *logPalette;
  WNDCLASS wndClass;
  LOGFONT logFont;
  ATOM a;
  HDC hDC;
  TEXTMETRIC textMetric;
  HGLOBAL oldFont;
  RECT r;
  closeFlag = FALSE;

  altPressed = arrowsPressed = 0;
  if (hIcon == NULL)
    hIcon = LoadIcon (hInstance, "BIG");
  mouseButtons = 0;
  mouseX = 0;
  mouseY = 0;
  {
    static FARPROC proc;
    if (hModule2 == NULL)
      {
	hModule2 = LoadLibrary ("user32");
	proc = GetProcAddress (hModule2, "RegisterClassExA");
      }
    if (proc != NULL)
      {
	WNDCLASSEX ExWndClass;
	memset (&ExWndClass, 0, sizeof (WNDCLASSEX));
	if (hIconSm == NULL)
	  hIconSm = LoadIcon (hInstance, "SMALL");
	ExWndClass.hIconSm = hIconSm;
	memset (&ExWndClass, 0, sizeof (WNDCLASSEX));
	ExWndClass.style = CS_OWNDC;
	ExWndClass.cbSize = sizeof (WNDCLASSEX);
	ExWndClass.lpfnWndProc = WindowProc;
	ExWndClass.hInstance = hInstance;
	ExWndClass.hIcon = hIcon;
	ExWndClass.hCursor = LoadCursor (NULL, IDC_ARROW);
	ExWndClass.lpszClassName = "XaosWindow";
	ExWndClass.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
	a = (ATOM) proc (&ExWndClass);
      }
    else
      {
	memset (&wndClass, 0, sizeof (WNDCLASS));
	wndClass.style = CS_OWNDC;
	wndClass.lpfnWndProc = WindowProc;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = hIcon;
	wndClass.hCursor = LoadCursor (NULL, IDC_ARROW);
	wndClass.lpszClassName = "XaosWindow";
	wndClass.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
	a = RegisterClass (&wndClass);
      }
  }
  if (!a)
    {
      x_error ("Unable to create windows class");
      return 0;
    }

  /* First time use defaut size, otherwise use saved sizes */
  if (sscanf (size, "%ix%ix", &width, &height) != 2)
    {
      width = WWIDTH;
      height = WHEIGHT;
    }
  if (windowpos)
    {
      xpos = rcWindow.left;
      ypos = rcWindow.top;
      width = rcWindow.right - rcWindow.left;
      height = rcWindow.bottom - rcWindow.top;
    }

  /* create main window */
  if (directX == DXFULLSCREEN)
    hWnd = CreateWindowEx (WS_EX_TOPMOST, "XaoSWindow", "XaoS", WS_POPUP,
			   0, 0, GetSystemMetrics (SM_CXSCREEN),
			   GetSystemMetrics (SM_CYSCREEN), NULL, NULL,
			   hInstance, NULL);
  else

    hWnd = CreateWindowEx (WS_EX_CLIENTEDGE, "XaoSWindow", "XaoS",
			   WS_OVERLAPPEDWINDOW | WS_EX_LEFTSCROLLBAR, xpos,
			   ypos, width, height, NULL, NULL, hInstance, NULL);

  if (!hWnd)
    {
      x_error ("Unable to create app window");
      return 0;
    }

  clipboard_format = RegisterClipboardFormat ("image/x-xaos.position");


  /* create font */
  memset (&logFont, 0, sizeof (LOGFONT));
  hDC = CreateDC ("DISPLAY", NULL, NULL, NULL);
  logFont.lfHeight = -MulDiv (12, GetDeviceCaps (hDC, LOGPIXELSY), 72);
  logFont.lfWeight = FW_NORMAL;
  logFont.lfPitchAndFamily = FIXED_PITCH;
  strcpy (logFont.lfFaceName, "Courier");

  hFont = CreateFontIndirect (&logFont);
  oldFont = SelectObject (hDC, hFont);
  GetTextMetrics (hDC, &textMetric);
  SelectObject (hDC, oldFont);
  DeleteDC (hDC);

  fontHeight = textMetric.tmHeight;
  fontWidth = textMetric.tmAveCharWidth;

  ShowWindow (hWnd, SW_NORMAL);

  GetClientRect (hWnd, &r);
  displayX = r.right;
  displayY = r.bottom;

  /* create palette */
  CalculateBITMAPINFO ();	/* calculate BITMAPINFO structure */
  logPalette = malloc (sizeof (LOGPALETTE) + 4 * 256);
  logPalette->palVersion = 0x300;
  logPalette->palNumEntries = 256;
  memcpy (logPalette->palPalEntry, bmp->bmiColors, 4 * 256);
  hPalette = CreatePalette (logPalette);
  free (logPalette);

  /* select and realize palette */
  hDC = GetDC (hWnd);
  SelectPalette (hDC, hPalette, FALSE);
  RealizePalette (hDC);
  ReleaseDC (hWnd, hDC);

  // increase priority of XaoS
  SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_ABOVE_NORMAL);

  MyHelpMsg = RegisterWindowMessage (HELPMSGSTRING);

  return 1;
}

static void
DeInitWindow ()
{
  if (tmpcaptured)
    ReleaseCapture ();
  if (directX != DXFULLSCREEN)
    windowpos = 1, GetWindowRect (hWnd, &rcWindow);
  /* normalize priority */
  SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_NORMAL);
  if (helpname)
    WinHelp (hWnd, helpname, HELP_QUIT, 0), free (helpname), helpname = NULL;

  /* destroy windows and other objects */
  DeleteObject (hFont);
  DestroyWindow (hWnd);
  UnregisterClass ("XaosWindow", hInstance);
  if (bmp)
    {
      free (bmp);
      bmp = NULL;
    }
  if (hModule2 != NULL)
    FreeLibrary (hModule2);
  hModule2 = NULL;
  win32_uninitializewindows ();
}

/* Display buffer to screen */
static void
Paint (HDC hDC)
{
  if (!initialized || !buffer1)
    return;
  StretchDIBits (hDC, 0, 0, displayX, displayY,
		 0, 0, displayX, displayY,
		 (currentbuff == 0) ? buffer1 : buffer2,
		 bmp, DIB_RGB_COLORS, SRCCOPY);
  needredraw = 0;
}



static int
Init (void)
{
  HDC hDC;
  buffer1 = buffer2 = NULL;

  // get bit depth
  hDC = CreateDC ("DISPLAY", NULL, NULL, NULL);
  bitDepth = GetDeviceCaps (hDC, BITSPIXEL);
  if (bitDepth < 8)
    bitDepth = 16;
  if (bitDepth < 10)
    bitDepth = 8;
  if (bitDepth >= 10 && bitDepth < 20)
    bitDepth = 16;
  if (bitDepth >= 20 && bitDepth < 28)
    bitDepth = 24;
  if (bitDepth >= 32 && bitDepth < 32)
    bitDepth = 32;
  DeleteDC (hDC);

  // create windows and other objects
  if (!InitWindow ())
    return 0;

  CalculateBITMAPINFO ();	/* calculate BITMAPINFO structure */


  return 1;
}

static void
getmouse (int *mx, int *my, int *mb)
{
  *mb = 0;
  if (mouseButtons & MK_LBUTTON)
    *mb |= 256;
  if (mouseButtons & MK_MBUTTON)
    *mb |= 512;
  if (mouseButtons & MK_RBUTTON)
    *mb |= 1024;
  *mx = mouseX;
  *my = mouseY;
}

static void
Processevents (int wait, int *mx, int *my, int *mb, int *k, int *c)
{
  MSG msg;
  int r;
  if (wait)
    {
      // wait for message if in wait mode
      r = GetMessage (&msg, hWnd, 0, 0);
      wait = 0;
    }
  else
    {
      // don't wait for message
      r = PeekMessage (&msg, hWnd, 0, 0, PM_REMOVE);
    }
#if 0
  if (needredraw)
    {
      if (directX)
	PaintDD ();
      else
	win32_display ();
    }
#endif
  if (r > 0)
    {
      if (msg.message == WM_CHAR)
	{
	  // ascii char
	  *c = msg.wParam;
	}
      if (msg.message == WM_KEYUP)
	switch (msg.wParam)
	  {
	  case VK_MENU:
	    altPressed = 0;
	    break;
	  case VK_UP:
	    arrowsPressed &= ~4;
	    break;
	  case VK_DOWN:
	    arrowsPressed &= ~8;
	    break;
	  case VK_LEFT:
	    arrowsPressed &= ~1;
	    break;
	  case VK_RIGHT:
	    arrowsPressed &= ~2;
	    break;
	  }
      if (msg.message == WM_KEYDOWN)
	{
	  // any key
	  switch (msg.wParam)
	    {
	    case VK_MENU:
	      /*x_message("Alt"); */
	      altPressed = 1;
	      break;
	    case VK_UP:
	      *c = UIKEY_UP;
	      arrowsPressed |= 4;
	      break;
	    case VK_DOWN:
	      *c = UIKEY_DOWN;
	      arrowsPressed |= 8;
	      break;
	    case VK_LEFT:
	      *c = UIKEY_LEFT;
	      arrowsPressed |= 1;
	      break;
	    case VK_RIGHT:
	      *c = UIKEY_RIGHT;
	      arrowsPressed |= 2;
	      break;
	    case VK_ESCAPE:
	      *c = UIKEY_ESC;
	      break;
	    case VK_BACK:
	      *c = UIKEY_BACKSPACE;
	      break;
	    case VK_TAB:
	      *c = UIKEY_TAB;
	      break;
	    case VK_HOME:
	      *c = UIKEY_HOME;
	      break;
	    case VK_END:
	      *c = UIKEY_END;
	      break;
	    case VK_PRIOR:
	      *c = UIKEY_PGUP;
	      break;
	    case VK_NEXT:
	      *c = UIKEY_PGDOWN;
	      break;
#ifdef DDRAW_DRIVER
	    case VK_RETURN:
	      /*x_message("Enter %i",altPressed); */
	      if (altPressed)
		{
		  HDC hDC;
		  CONST char *cmd;
		  CONST menuitem *item;
		  if (directX == DXFULLSCREEN)
		    {
		      int depth;
		      cmd = "dX-windowed";
		      hDC = CreateDC ("DISPLAY", NULL, NULL, NULL);
		      depth = GetDeviceCaps (hDC, BITSPIXEL);
		      DeleteDC (hDC);
		      if (!DXSUPPORTEDDEPTH (0, depth))
			cmd = "win32";
		    }
		  else
		    {
		      cmd = "dX-fullscreen";
		    }
		  item = menu_findcommand (cmd);
		  ui_menuactivate (item, NULL);
		}
	      break;
#endif
	    }
	}
      // forward messages to window
      TranslateMessage (&msg);
      DispatchMessage (&msg);
    }
  getmouse (mx, my, mb);	// get mouse position

  *k = arrowsPressed;

  if (closeFlag)
    *c = -2;			// force quit if so requested

}

// calculate BITMAPINFO structure. It is used to copy bitmaps
static void
CalculateBITMAPINFO ()
{
  int i;
  if (!bmp)
    bmp = (BITMAPINFO *) malloc (sizeof (BITMAPINFOHEADER) + 4 * 256);

  memset (bmp, 0, sizeof (BITMAPINFOHEADER));
  bmp->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
  bmp->bmiHeader.biWidth = displayX;
  bmp->bmiHeader.biHeight = -displayY;
  bmp->bmiHeader.biPlanes = 1;
  bmp->bmiHeader.biBitCount = bitDepth;

  // create default palette
  for (i = 0; i < 256; i++)
    {
      bmp->bmiColors[i].rgbRed = i;
      bmp->bmiColors[i].rgbGreen = i;
      bmp->bmiColors[i].rgbBlue = i;
    }
}

#ifdef DDRAW_DRIVER

/**************************************************************************************
                             DirectDraw driver helper routines
 */
static char *resstr[MAXRESOLUTIONS];
static struct resolutions
{
  int width, height;
}
ressize[MAXRESOLUTIONS];
static int nresolutions;
/* callback for DirectX resolutions */
static HRESULT WINAPI
EnumModesCallback (LPDDSURFACEDESC lpDDSurfaceDesc, LPVOID lpContext)
{
  if (nresolutions < MAXRESOLUTIONS)
    if (lpDDSurfaceDesc->ddpfPixelFormat.u1.dwRGBBitCount == 8 ||
	lpDDSurfaceDesc->ddpfPixelFormat.u1.dwRGBBitCount == 16 ||
	lpDDSurfaceDesc->ddpfPixelFormat.u1.dwRGBBitCount == 24 ||
	lpDDSurfaceDesc->ddpfPixelFormat.u1.dwRGBBitCount == 32)
      {
	int i;
	char s[20];
	for (i = 0; i < nresolutions; i++)
	  if ((int) ressize[i].width == (int) lpDDSurfaceDesc->dwWidth
	      && (int) ressize[i].height == (int) lpDDSurfaceDesc->dwHeight)
	    return DDENUMRET_OK;
	ressize[nresolutions].width = lpDDSurfaceDesc->dwWidth;
	ressize[nresolutions].height = lpDDSurfaceDesc->dwHeight;
	sprintf (s, "%ix%i", lpDDSurfaceDesc->dwWidth,
		 lpDDSurfaceDesc->dwHeight);
	resstr[nresolutions] = strdup (s);
	nresolutions++;
      }
  return DDENUMRET_OK;
}
typedef HRESULT WINAPI (*ddrawcreateptr) (GUID FAR * lpGUID,
					  LPDIRECTDRAW FAR * lplpDD,
					  IUnknown FAR * pUnkOuter);
static ddrawcreateptr DirectDrawCreatePtr;

static int
ResizeDD (int fullscreen)
{
  HRESULT ddrval;
  DDSURFACEDESC ddsd;
  /*DDCAPS2 ddscaps; */
  LPDIRECTDRAWCLIPPER pClipper;
  int dxwidth;
  int dxheight;
  int dxbpp;

  // free DirectX objects
  if (lpSurfaces[0])
    IDirectDrawSurface_Release (lpSurfaces[0]);
  lpSurfaces[0] = NULL;
  if (dxPalette)
    IDirectDrawPalette_Release (dxPalette);
  dxPalette = NULL;
  /* Set cooperative level */
  ddrval = IDirectDraw2_SetCooperativeLevel (lpDD2, hWnd,
					     fullscreen ? (DDSCL_FULLSCREEN |
							   DDSCL_EXCLUSIVE |
							   DDSCL_ALLOWREBOOT)
					     : DDSCL_NORMAL);
  if (ddrval != DD_OK)
    {
      DeInitDD ();
      x_error ("Failed to set cooperative level");
      return 0;
    }

  if (fullscreen)
    {
      if (sscanf (dxsize, "%ix%ix%i", &dxwidth, &dxheight, &dxbpp) != 3)
	{
	  dxwidth = DXWIDTH;
	  dxheight = DXHEIGHT;
	  dxbpp = DXBPP;
	}
      displayX = dxwidth;
      displayY = dxheight;
      bitDepth = dxbpp;
      if (bitDepth < 10)
	bitDepth = 8;
      if (bitDepth >= 10 && bitDepth < 20)
	bitDepth = 16;
      if (bitDepth >= 20 && bitDepth < 28)
	bitDepth = 24;
      if (bitDepth >= 32 && bitDepth < 32)
	bitDepth = 32;

      /* set resolution and bit depth */
      ddrval =
	IDirectDraw2_SetDisplayMode (lpDD2, displayX, displayY, bitDepth, 0,
				     0);
      if (ddrval != DD_OK)
	{
	  /* The display mode cannot be changed. 
	     The mode is either not supported or 
	     another application has exclusive mode.

	     Try 320x200x256 and 640x480x256 modes before giving up */
	  displayX = 320;
	  displayY = 200;
	  bitDepth = 8;
	  ddrval =
	    IDirectDraw2_SetDisplayMode (lpDD2, displayX, displayY, bitDepth,
					 0, 0);
	  if (ddrval != DD_OK)
	    {
	      displayY = 240;
	      if (ddrval != DD_OK)
		{
		  displayX = 640;
		  displayY = 480;
		  ddrval =
		    IDirectDraw2_SetDisplayMode (lpDD2, displayX, displayY,
						 bitDepth, 0, 0);
		  if (ddrval != DD_OK)
		    {
		      /* Bad luck... give up. */
		      DeInitDD ();
		      return 0;
		    }
		}
	    }
	}
      SetRect (&rcViewport, 0, 0, displayX, displayY);
      rcScreen = rcViewport;
    }
  else
    {
      /* Get the dimensions of the viewport and screen bounds */
      GetClientRect (hWnd, &rcViewport);
      GetClientRect (hWnd, &rcScreen);
      ClientToScreen (hWnd, (POINT *) & rcScreen.left);
      ClientToScreen (hWnd, (POINT *) & rcScreen.right);
      /*bitDepth = GetDeviceCaps (hDC, BITSPIXEL); */

      /* Create clipper object for window */
      ddrval = IDirectDraw_CreateClipper (lpDD, 0, &pClipper, NULL);
      if (ddrval != DD_OK)
	{
	  DeInitDD ();
	  x_error ("Failed to create clipper object");
	  return 0;
	}
      /* Asociate it */
      IDirectDrawClipper_SetHWnd (pClipper, 0, hWnd);
    }
  /* Create the primary surface with one back buffer */
  CalculateBITMAPINFO ();	// calculate BITMAPINFO structure

  memset (&ddsd, 0, sizeof (ddsd));
  ddsd.dwSize = sizeof (ddsd);
  ddsd.dwFlags = DDSD_CAPS;
  ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

  ddrval = IDirectDraw_CreateSurface (lpDD, &ddsd, &lpSurfaces[0], NULL);
  if (ddrval != DD_OK)
    {
      DeInitDD ();
      x_error ("Failed to create flipping surface");
      return 0;
    }

  if (!fullscreen)
    {
      IDirectDrawSurface_SetClipper (lpSurfaces[0], pClipper);
      IDirectDrawClipper_Release (pClipper);
      if (IDirectDrawSurface_GetSurfaceDesc (lpSurfaces[0], &ddsd) != DD_OK)
	{
	  DeInitDD ();
	  x_error ("Failed to get pixel format");
	  return 0;
	}
      bitDepth = ddsd.ddpfPixelFormat.u1.dwRGBBitCount;
    }

  if (bitDepth == 8)
    {
      /* create palette */
      ddrval =
	IDirectDraw_CreatePalette (lpDD, DDPCAPS_8BIT,
				   (LPPALETTEENTRY) bmp->bmiColors,
				   &dxPalette, NULL);
      if (ddrval != DD_OK)
	{
	  DeInitDD ();
	  x_error ("Failed to create palette");
	  return 0;
	}

      /* set palette */
      IDirectDrawSurface_SetPalette (lpSurfaces[0], dxPalette);
    }
  if (fullscreen)
    SetCursor (NULL);
  needredraw = 1;
  return 1;

}

/* init DirectX */
static int
InitDD (int fullscreen)
{
  HRESULT ddrval;
  HDC hDC;
  directX = fullscreen ? DXFULLSCREEN : DXWINDOWED;

  if (!hModule)
    hModule = LoadLibrary ("ddraw");
  if (!hModule)
    {
      /*x_error ("Unable to load DirectX (ddraw.dll)"); */
      return 0;
    }
  /* DirectDraw don't support 16 color modes. Don't even try to initialize
     it then. Also avoid unsupported bit depths in the windowed driver */
  hDC = CreateDC ("DISPLAY", NULL, NULL, NULL);
  bitDepth = GetDeviceCaps (hDC, BITSPIXEL);
  DeleteDC (hDC);

  if (!DXSUPPORTEDDEPTH (fullscreen, bitDepth))
    return 0;


  DirectDrawCreatePtr =
    (ddrawcreateptr) GetProcAddress (hModule, "DirectDrawCreate");
  if (!DirectDrawCreatePtr)
    {
      x_error
	("Unable to get hook DirectDrawCreate in ddraw.dll. Check your DirectX installation");
      return 0;
    }

  lpDD = NULL;
  lpDD2 = NULL;
  lpSurfaces[0] = NULL;
  lpSurfaces[1] = NULL;
  buffer1 = buffer2 = NULL;

  bitDepth = 8;

  InitWindow ();
  UpdateWindow (hWnd);
  SetFocus (hWnd);


  /* contact DirectX */
  ddrval = DirectDrawCreatePtr (NULL, &lpDD, NULL);
  if (ddrval != DD_OK)
    {
      DeInitDD ();
      x_error ("Failed to create DirectDraw object");
      return 0;
    }

  /* get IDirectDraw2 interface */
  ddrval =
    IDirectDraw_QueryInterface (lpDD, &IID_IDirectDraw2, (LPVOID *) & lpDD2);
  if (ddrval != DD_OK)
    {
      DeInitDD ();
      x_error ("Failed to get DirectDraw2 object");
      return 0;
    }
  /* enumerate modes */
#ifdef DDRAW_DRIVER
  if (!nresolutions && directX == DXFULLSCREEN)
    IDirectDraw2_EnumDisplayModes (lpDD2, 0, NULL, NULL, EnumModesCallback);
#endif


  if (!ResizeDD (fullscreen))
    return 0;
  if (fullscreen)
    {
      SetCapture (hWnd);	// make sure no other windows get mouse messages

      captured = 1;
    }

  return 1;
}

/* uninitialize DirectX */
static void
DeInitDD (void)
{
  if (captured)
    ReleaseCapture (), captured = 0;	// free mouse

  // free DirectX objects
  if (lpSurfaces[0])
    IDirectDrawSurface_Release (lpSurfaces[0]);
  lpSurfaces[0] = NULL;
  if (BackSurface[0])
    IDirectDrawSurface_Release (BackSurface[0]);
  BackSurface[0] = NULL;
  if (BackSurface[1])
    IDirectDrawSurface_Release (BackSurface[1]);
  BackSurface[1] = NULL;
  if (dxPalette)
    IDirectDrawPalette_Release (dxPalette);
  dxPalette = NULL;
  if (lpDD2)
    IDirectDraw2_Release (lpDD2);
  lpDD2 = NULL;
  if (lpDD)
    IDirectDraw_Release (lpDD);
  lpDD = NULL;
  DeInitWindow ();
  if (hModule != NULL)
    FreeLibrary (hModule), hModule = NULL;
  hWnd = NULL;
  directX = 0;
}

static LRESULT CALLBACK WindowProc (HWND hwnd,	// handle to window
				    UINT uMsg,	// message identifier
				    WPARAM wParam,	// first message parameter
				    LPARAM lParam	// second message parameter
  );
static void
UpdateMouseDD ()
{
  DDSURFACEDESC m_surface;
  PUCHAR dst;
  DWORD ddrval;
  memset (&m_surface, 0, sizeof (DDSURFACEDESC));
  m_surface.dwSize = sizeof (DDSURFACEDESC);
  ddrval = IDirectDrawSurface_Lock (lpSurfaces[0], NULL, &m_surface,
				    DDLOCK_WAIT, NULL);
  if (ddrval != DD_OK)
    {
      return;
    }

  dst = (PUCHAR) m_surface.lpSurface;
  if (storeddata)
    {
      restore (dst, storeddata, bitDepth, m_surface.u1.lPitch, displayX,
	       displayY, oldmouseX, oldmouseY);
      free (storeddata);
    }
  storeddata =
    store (dst, bitDepth, m_surface.u1.lPitch, displayX, displayY, mouseX,
	   mouseY);
  drawmouse (dst, mousepointer, bitDepth, m_surface.u1.lPitch, displayX,
	     displayY, mouseX, mouseY);
  oldmouseX = mouseX;
  oldmouseY = mouseY;
  IDirectDrawSurface_Unlock (lpSurfaces[0], m_surface.lpSurface);
}

/* Display buffer */
static void
PaintDD ()
{
  DWORD ddrval;
  if (!IsWindowVisible (hWnd) || !active || !initialized || !BackSurface[0])
    return;
  IDirectDrawSurface_Unlock (BackSurface[0], surface[0].lpSurface);
  IDirectDrawSurface_Unlock (BackSurface[1], surface[1].lpSurface);
  if (directX == DXFULLSCREEN)
    {
      if (storeddata)
	free (storeddata), storeddata = NULL;
      storeddata =
	store (currentbuff ? buffer2 : buffer1, bitDepth, lineSize, displayX,
	       displayY, mouseX, mouseY);
      drawmouse (currentbuff ? buffer2 : buffer1, mousepointer, bitDepth,
		 lineSize, displayX, displayY, mouseX, mouseY);
      ddrval =
	IDirectDrawSurface_BltFast (lpSurfaces[0], 0, 0,
				    BackSurface[currentbuff], &rcScreen,
				    FALSE);
      restore (currentbuff ? buffer2 : buffer1, storeddata, bitDepth,
	       lineSize, displayX, displayY, mouseX, mouseY);
      oldmouseX = mouseX;
      oldmouseY = mouseY;
    }
  else
    {
      ddrval = IDirectDrawSurface_Blt (lpSurfaces[0], &rcScreen,
				       BackSurface[currentbuff], &rcViewport,
				       DDBLT_WAIT, NULL);
    }
  if (ddrval != DD_OK)
    {
      if ((int) ddrval == (int) DDERR_SURFACELOST)
	{
	  IDirectDrawSurface_Restore (lpSurfaces[0]);
	  IDirectDrawSurface_Restore (BackSurface[0]);
	  IDirectDrawSurface_Restore (BackSurface[1]);
	  ddrval = IDirectDrawSurface_Blt (lpSurfaces[0], &rcScreen,
					   BackSurface[currentbuff],
					   &rcViewport, DDBLT_WAIT, NULL);
	  //if (ddrval == DDERR_SURFACELOST) resized=1; /*We've lost our fractal*/
	}
    }
  ddrval = IDirectDrawSurface_Lock (BackSurface[0], NULL, &surface[0],
				    DDLOCK_WAIT, NULL);
  ddrval = IDirectDrawSurface_Lock (BackSurface[1], NULL, &surface[1],
				    DDLOCK_WAIT, NULL);
  if (buffer1 != (char *) surface[0].lpSurface ||
      buffer2 != (char *) surface[1].lpSurface)
    {
      DeInitDD ();
      x_fatalerror
	("Unexpected event - buffers moved! Please contact authors!");
    }
  needredraw = 0;

}
#endif

/**************************************************************************************
                             Drivers implementation
 */



static void
flip_buffers (void)
{
  currentbuff ^= 1;
}


static void
processevents (int wait, int *mx, int *my, int *mb, int *k)
{
  int c = -1;
  *mb = 0;
  *k = 0;
  Processevents (wait, mx, my, mb, k, &c);
  if (c > -1)
    {
      ui_key (c);
    }

  if (c == -2)
    ui_quit ();			// -2 signals program exit

  if (resized)
    {
      ui_resize ();		// tell Xaos to resize

    }
}
static void
print (int x, int y, CONST char *text)
{
  HDC hDC;
  static char current[256];
  char s[256];
#ifdef DDRAW_DRIVER
  if (directX == DXFULLSCREEN)
    {
      HGLOBAL oldFont;
      if (IDirectDrawSurface_GetDC (lpSurfaces[0], &hDC) != DD_OK)
	return;
      SetTextColor (hDC, 0xffffff);
      SetBkColor (hDC, 0x000000);
      oldFont = SelectObject (hDC, hFont);
      ExtTextOut (hDC, x, y, 0, NULL, text, strlen (text), NULL);
      SelectObject (hDC, oldFont);
      IDirectDrawSurface_ReleaseDC (lpSurfaces[0], hDC);
      return;
    }
#endif
  if (!text[0])
    strcpy (s, "XaoS");
  else
    sprintf (s, "XaoS - %s", text);
  if (strcmp (current, s))
    strcpy (current, s), SetWindowText (hWnd, s);
}

static void
mousetype (int type)
{
  char *cursor;
  switch (type)
    {
    default:
    case 0:
      cursor = IDC_ARROW;
      break;
    case 1:
      cursor = IDC_WAIT;
      break;
    case 2:
      cursor = IDC_NO;
      break;
    }
  SetCursor (LoadCursor (NULL, cursor));
}
static void
set_palette (ui_palette pal1, int start, int end)
{
  PUCHAR pal = (PUCHAR) pal1;
  HDC hDC;
  int i;
  // store new palette entries locally
  memcpy (backpalette + 4 * start, pal, (end - start) * 4);
  for (i = start; i <= end; i++)
    {
      bmp->bmiColors[i].rgbRed = *(pal + 4 * (i - start) + 0);
      bmp->bmiColors[i].rgbGreen = *(pal + 4 * (i - start) + 1);
      bmp->bmiColors[i].rgbBlue = *(pal + 4 * (i - start) + 2);
      bmp->bmiColors[i].rgbReserved = 0;
    }
  // update window/screen
#ifdef DDRAW_DRIVER
  if (directX)
    {
      IDirectDrawPalette_SetEntries (dxPalette, 0, start, end - start + 1,
				     (PALETTEENTRY *) pal);
    }
  else
#endif
    {
      SetPaletteEntries (hPalette, start, end - start + 1,
			 (PALETTEENTRY *) pal);
      hDC = GetDC (hWnd);
      UnrealizeObject (hPalette);
      RealizePalette (hDC);
      ReleaseDC (hWnd, hDC);
      win32_display ();
    }
}

static void
win32_copy (struct uih_context *uih)
{
  char *c = ui_getpos ();
  HANDLE hData = GlobalAlloc (GMEM_DDESHARE, strlen (c) + 1);
  char *data;
  if (!hData)
    {
      x_error ("Out of memory");
      free (c);
      return;
    }
  if (!(data = GlobalLock (hData)))
    {
      x_error ("Out of memory");
      free (c);
      return;
    }
  memcpy (hData, c, strlen (c) + 1);
  GlobalUnlock (hData);
  if (OpenClipboard (hWnd))
    {
      EmptyClipboard ();
      SetClipboardData (clipboard_format, hData);
    }
  free (c);
}
static void
win32_paste (void)
{
  if (OpenClipboard (hWnd))
    {
      HANDLE hClipData;
      char *text;
      if (!(hClipData = GetClipboardData (clipboard_format)))
	{
	  CloseClipboard ();
	  return;
	}
      if (!(text = GlobalLock (hClipData)))
	{
	  x_error ("Out of memory");
	  CloseClipboard ();
	}
      ui_loadstr (strdup (text));
      GlobalUnlock (hClipData);
      CloseClipboard ();
    }
}

#define MAX_MENUITEMS_I18N 20
static menuitem menuitems_i18n[MAX_MENUITEMS_I18N];

int uiw_no_menuitems_i18n = 0, uiw_no_cutpasteitems_i18n;

static menuitem *cutpasteitems;
static void
add_cutpasteitems ()
{
  // General method (not needed currently):
  int no_menuitems_i18n = uiw_no_menuitems_i18n;	/* This variable must be local. */
  MENUSEPARATOR_I ("edit");
  MENUNOP_I ("edit", NULL, gettext ("Copy"), "copy", 0, win32_copy);
  MENUNOP_I ("edit", NULL, gettext ("Paste"), "paste", 0, win32_paste);
  MENUSEPARATOR_I ("helpmenu");
  MENUNOP_I ("helpmenu", NULL, gettext ("About"), "about", 0, AboutBox);
  no_menuitems_i18n -= uiw_no_menuitems_i18n;
  cutpasteitems = &(menuitems_i18n[uiw_no_menuitems_i18n]);
  uiw_no_cutpasteitems_i18n = no_menuitems_i18n;
  menu_add (cutpasteitems, uiw_no_cutpasteitems_i18n);
  uiw_no_menuitems_i18n += no_menuitems_i18n;

}


static int
win32_init (void)
{
  int r;

#ifdef DDRAW_DRIVER
  directX = 0;
#endif
  r = Init ();
  if (!r)
    return r;
  win32_driver.textwidth = fontWidth;
  win32_driver.textheight = fontHeight;
  getres (&win32_driver.width, &win32_driver.height);
  win32_createrootmenu ();
  add_cutpasteitems ();
  return r;
}

static void
win32_uninitialize (void)
{
  DeInitWindow ();
  menu_delete (cutpasteitems, uiw_no_cutpasteitems_i18n);
}
static void
win32_getsize (int *width, int *height)
{
  resized = 0;
  *width = displayX;
  *height = displayY;
  switch (bitDepth)
    {
    case 8:
      win32_driver.imagetype = UI_C256;
      break;
    case 16:
      /* Windows seems to always use 15bpp mode */
      win32_driver.imagetype = UI_TRUECOLOR16;
      win32_driver.rmask = 31 * 32 * 32;
      win32_driver.gmask = 31 * 32;
      win32_driver.bmask = 31;
      break;
    case 24:
      win32_driver.imagetype = UI_TRUECOLOR24;
      win32_driver.rmask = 0xff0000;
      win32_driver.gmask = 0x00ff00;
      win32_driver.bmask = 0x0000ff;
      break;
    case 32:
      win32_driver.imagetype = UI_TRUECOLOR;
      win32_driver.rmask = 0xff0000;
      win32_driver.gmask = 0x00ff00;
      win32_driver.bmask = 0x0000ff;
      break;
    }
  CalculateBITMAPINFO ();
}



static void
win32_display ()
{
  HDC hDC = GetDC (hWnd);
  if (IsWindowVisible (hWnd))
    Paint (hDC);
  ReleaseDC (hWnd, hDC);
}
static int
win32_alloc_buffers (char **b1, char **b2)
{
  currentbuff = 0;
  // calculate DWORD aligned line length
  lineSize = displayX * ((bitDepth + 7) / 8);
  lineSize += 3 - ((lineSize - 1) & 3);

  buffer1 = (char *) malloc (displayY * lineSize);
  buffer2 = (char *) malloc (displayY * lineSize);
  *b1 = buffer1;
  *b2 = buffer2;
  initialized = 1;
  return lineSize;
}

static void
win32_free_buffers (char *b1, char *b2)
{
  initialized = 0;
  free (buffer1);
  free (buffer2);
  buffer1 = buffer2 = NULL;
}
static CONST char *CONST dx_depth[] = { "8bpp (256 colors)",
  "16bpp (65536 colors)",
  "24bpp (16777216 colors)",
  "32bpp (16777216 colors)",
  NULL
};

#ifdef DDRAW_DRIVER
static menudialog dx_resdialog[] = {
  DIALOGCHOICE ("Resolution", resstr, 0),
  DIALOGCHOICE ("Depth", dx_depth, 0),
  {NULL}
};
static menudialog *
dx_resizedialog (struct uih_context *c)
{
  int i;
  switch (bitDepth)
    {
    case 8:
      dx_resdialog[1].defint = 0;
      break;
    case 16:
      dx_resdialog[1].defint = 1;
      break;
    case 24:
      dx_resdialog[1].defint = 2;
      break;
    case 32:
      dx_resdialog[1].defint = 3;
    }
  for (i = 0; i < MAXRESOLUTIONS; i++)
    if (displayX == ressize[i].width && displayY == ressize[i].height)
      {
	dx_resdialog[0].defint = i;
	break;
      }
  return dx_resdialog;
}
static void
dx_resize (struct uih_context *c, dialogparam * p)
{
  static char s[10];
  CONST static char *CONST st[] = { "8", "16", "24", "32" };
  sprintf (s, "%sx%s", resstr[p[0].dint], st[p[1].dint]);
  dxsize = s;
  resized = 1;
  ui_call_resize ();
}

int uiw_no_resizeitems_i18n;

static menuitem *resizeitems;

static void
add_resizeitems ()
{
  // General method, it's needed:
  int no_menuitems_i18n = uiw_no_menuitems_i18n;	/* This variable must be local. */
  MENUCDIALOG_I ("ui", "=", gettext ("Resize"), "resize", 0, dx_resize,
		 dx_resizedialog);
  MENUNOP_I ("misc", NULL, "Generate .dlg files", "genresources", 0,
	     win32_genresources);
  no_menuitems_i18n -= uiw_no_menuitems_i18n;
  resizeitems = &(menuitems_i18n[uiw_no_menuitems_i18n]);
  uiw_no_resizeitems_i18n = no_menuitems_i18n;
  menu_add (resizeitems, uiw_no_resizeitems_i18n);
  uiw_no_menuitems_i18n += no_menuitems_i18n;

}


static int
dx_alloc_buffers (char **b1, char **b2)
{
  DWORD ddrval;
  DDSURFACEDESC ddsd;
  int i;
  currentbuff = 0;
  memset (surface, 0, sizeof (DDSURFACEDESC) * 2);
  memset (&ddsd, 0, sizeof (DDSURFACEDESC));
  ddsd.dwSize = sizeof (ddsd);
  if (IDirectDrawSurface_GetSurfaceDesc (lpSurfaces[0], &ddsd) != DD_OK)
    {
      DeInitDD ();
      x_error ("Failed to get pixel format");
      return 0;
    }
  for (i = 0; i < 2; i++)
    {
      ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS;
      ddsd.dwWidth = displayX;
      ddsd.dwHeight = displayY;
      ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;

      ddrval = IDirectDraw_CreateSurface (lpDD, &ddsd, &BackSurface[i], NULL);
      if (ddrval != DD_OK)
	{
	  DeInitDD ();
	  x_error ("Failed to create back surface");
	  return 0;
	}
    }
  for (i = 0; i < 2; i++)
    {
      surface[i].dwSize = sizeof (DDSURFACEDESC);
      ddrval = IDirectDrawSurface_Lock (BackSurface[i], NULL, surface + i,
					DDLOCK_WAIT, NULL);
      if (ddrval != DD_OK)
	{
	  DeInitDD ();
	  x_fatalerror ("Failed to lock offscreen surfaces");
	}
    }
  buffer1 = *b1 = (char *) surface[0].lpSurface;
  buffer2 = *b2 = (char *) surface[1].lpSurface;
  lineSize = surface[0].u1.lPitch;
  initialized = 1;
  return lineSize;
}

static void
dx_free_buffers (char *b1, char *b2)
{
  IDirectDrawSurface_Unlock (BackSurface[0], surface[0].lpSurface);
  IDirectDrawSurface_Unlock (BackSurface[1], surface[1].lpSurface);
  if (BackSurface[0])
    IDirectDrawSurface_Release (BackSurface[0]);
  if (BackSurface[1])
    IDirectDrawSurface_Release (BackSurface[1]);
  BackSurface[0] = NULL;
  BackSurface[1] = NULL;
  initialized = 0;
  buffer1 = buffer2 = NULL;
}

static int
dx_imgparams (void)
{
  DDSURFACEDESC s;
  memset (&s, 0, sizeof (s));
  s.dwSize = sizeof (s);
  if (IDirectDrawSurface_GetSurfaceDesc (lpSurfaces[0], &s) != DD_OK)
    {
      DeInitDD ();
      x_error ("Failed to get pixel format");
      return 0;
    }
  switch (s.ddpfPixelFormat.u1.dwRGBBitCount)
    {
    case 8:
      dxw_driver.imagetype = UI_C256;
      dxf_driver.imagetype = UI_C256;
      break;
    case 16:
    case 15:
      dxw_driver.imagetype = UI_TRUECOLOR16;
      dxf_driver.imagetype = UI_TRUECOLOR16;
      break;
    case 24:
      dxw_driver.imagetype = UI_TRUECOLOR24;
      dxf_driver.imagetype = UI_TRUECOLOR24;
      break;
    case 32:
      dxw_driver.imagetype = UI_TRUECOLOR;
      dxf_driver.imagetype = UI_TRUECOLOR;
      break;
    default:
      x_fatalerror
	("Unsupported bit depth! Only 8bpp, 16bpp, 24bpp and 32bpp modes supported\n");
      return 0;
    }
  dxw_driver.rmask = s.ddpfPixelFormat.u2.dwRBitMask;
  dxw_driver.gmask = s.ddpfPixelFormat.u3.dwGBitMask;
  dxw_driver.bmask = s.ddpfPixelFormat.u4.dwBBitMask;
  dxf_driver.rmask = s.ddpfPixelFormat.u2.dwRBitMask;
  dxf_driver.gmask = s.ddpfPixelFormat.u3.dwGBitMask;
  dxf_driver.bmask = s.ddpfPixelFormat.u4.dwBBitMask;
  dxf_driver.textwidth = fontWidth;
  dxf_driver.textheight = fontHeight;
  dxw_driver.textwidth = fontWidth;
  dxw_driver.textheight = fontHeight;
  return 1;
}

static int
dxw_init (void)
{
  int r;

  r = InitDD (0);
  if (!r)
    return r;

  if (!dx_imgparams ())
    return 0;
  win32_createrootmenu ();
  getres (&dxw_driver.width, &dxw_driver.height);
  add_cutpasteitems ();
  return r;
}
static int
dxf_init (void)
{
  int r;

  getdimens (&dxf_driver.width, &dxf_driver.height);
  r = InitDD (1);
  if (!r)
    return r;

  if (!dx_imgparams ())
    return 0;
  add_resizeitems ();
  add_cutpasteitems ();
  return r;
}



static void
dx_uninitialize (void)
{
  if (directX == DXFULLSCREEN)
    menu_delete (resizeitems, uiw_no_resizeitems_i18n);
  menu_delete (cutpasteitems, uiw_no_cutpasteitems_i18n);
  DeInitDD ();
}




static void
dx_getsize (int *width, int *height)
{
  if (resized)
    {
      resized = 0;
      if (!ResizeDD (directX == DXFULLSCREEN))
	{
	  DeInitDD ();
	  x_fatalerror ("Failed to resize");
	}
      if (!dx_imgparams ())
	{
	  DeInitDD ();
	  x_fatalerror ("Internal program error #34234");
	}
    }
  *width = displayX;
  *height = displayY;
  CalculateBITMAPINFO ();
}
static void
dx_mousetype (int type)
{
  switch (type)
    {
    default:
    case 0:
      mousepointer = mouse_pointer_data;
      break;
    case 1:
      mousepointer = wait_pointer_data;
      break;
    case 2:
      mousepointer = replay_pointer_data;
      break;
    }
  UpdateMouseDD ();
}
#endif

void
win32_help (struct uih_context *c, CONST char *name)
{
  FILE *f;
  char *n;
  if (helpname == NULL)
    {
      if (!strcmp (name, "main"))
	name = "Contents";
      if (directX == DXFULLSCREEN)
	ShowWindow (hWnd, SW_MINIMIZE);
      n = xio_fixpath ("\01\\help\\xaoswin.hlp");
      if ((f = fopen (n, "r")))
	{
	  fclose (f);
	}
      else
	{
	  free (n);
	  n = xio_fixpath ("\01\\..\\help\\xaoswin.hlp");
	  if ((f = fopen (n, "r")))
	    {
	      fclose (f);
	    }
	  else
	    n = strdup ("..\\help\\xaoswin.hlp");
	}
      helpname = n;
    }
  if (!WinHelp (hWnd, helpname, HELP_PARTIALKEY, (ULONG) (CONST char *) name))
    {
      x_error ("Failed to start WinHelp on topic %s", name);
    }
}


static struct params params[] = {
  {"", P_HELP, NULL, "Win32 driver options:"},
  {"-size", P_STRING, &size, "Window size in format WIDTHxHEIGHT (320x200)"},
  {NULL, 0, NULL, NULL}
};
static struct params dxfparams[] = {
  {"", P_HELP, NULL, "DirectX fullscreen driver options:"},
  {"-mode", P_STRING, &dxsize,
   "Select preffered graphics mode in format WIDTHxHEIGHTxDEPTH  (320x200x8)"},
  {NULL, 0, NULL, NULL}
};
static struct params dxwparams[] = {
  {"", P_HELP, NULL, "DirectX windowed driver options:"},
  {"-size", P_STRING, &size, "Window size in format WIDTHxHEIGHT (320x200)"},
  {NULL, 0, NULL, NULL}
};

extern int XaoS_main (int argc, char **argv);
int STDCALL
WinMain (HINSTANCE hInstance1,
	 HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
  static char name0[256];
  static char *argv[256];
  int argc = 1;
  int i;

  GetModuleFileName (hInstance1, name0, 256);
  /* Allocate everything virtually - be on the safe side */
  argv[0] = strdup (name0);
  lpCmdLine = strdup (lpCmdLine);

  for (i = 0; lpCmdLine[i]; i++)
    {
      if (lpCmdLine[i] == ' ' || lpCmdLine[i] == '\t')
	lpCmdLine[i] = 0;
      else if (!i || !lpCmdLine[i - 1])
	argv[argc] = lpCmdLine + i, argc++;
    }

  hInstance = hInstance1;
  return XaoS_main (argc, argv);
}

static CONST struct gui_driver win32_gui_driver = {
  win32_dorootmenu,
  win32_enabledisable,
  win32_menu,
  win32_dialog,
  win32_help
};
static CONST struct gui_driver win32_fullscreen_gui_driver = {
  NULL,
  NULL,
  NULL,
  NULL,
  win32_help
};
struct ui_driver win32_driver = {
  "win32",
  win32_init,
  win32_getsize,
  processevents,
  getmouse,
  win32_uninitialize,
  NULL,				//     win32_set_color,
  set_palette,
  print,
  win32_display,
  win32_alloc_buffers,
  win32_free_buffers,
  flip_buffers,
  mousetype,
  NULL,
  16 + 16,
  12,
  params,
  PIXELSIZE | UPDATE_AFTER_PALETTE,
  0.0, 0.0,
  0, 0,
  UI_C256,
  0, 256, 255,
  0, 0, 0,
  &win32_gui_driver
};

#ifdef DDRAW_DRIVER
struct ui_driver dxw_driver = {
  "dX-window",
  dxw_init,
  dx_getsize,
  processevents,
  getmouse,
  dx_uninitialize,
  NULL,				//     dx_set_color,
  set_palette,
  print,
  PaintDD,
  dx_alloc_buffers,
  dx_free_buffers,
  flip_buffers,
  mousetype,
  NULL,
  16 + 16,
  12,
  dxwparams,
  PIXELSIZE,
  0.0, 0.0,
  0, 0,
  UI_C256,
  0, 256, 255,
  0, 0, 0,
  &win32_gui_driver
};
struct ui_driver dxf_driver = {
  "dX-fullscreen",
  dxf_init,
  dx_getsize,
  processevents,
  getmouse,
  dx_uninitialize,
  NULL,				//     dx_set_color,
  set_palette,
  print,
  PaintDD,
  dx_alloc_buffers,
  dx_free_buffers,
  flip_buffers,
  dx_mousetype,
  NULL,
  16 + 16,
  12,
  dxfparams,
  FULLSCREEN | SCREENSIZE,
  0.0, 0.0,
  0, 0,
  UI_C256,
  0, 256, 255,
  0, 0, 0,
  &win32_fullscreen_gui_driver
};
#endif

void
x_message (const char *text, ...)
{
  va_list ap;
  char buf[4096];
  va_start (ap, text);
  vsprintf (buf, text, ap);
  if (directX == DXFULLSCREEN)
    ShowWindow (hWnd, SW_MINIMIZE);
  MessageBox (NULL, buf, "XaoS", MB_OK | MB_ICONINFORMATION);
  va_end (ap);
}

void
x_error (const char *text, ...)
{
  va_list ap;
  char buf[4096];
  va_start (ap, text);
  vsprintf (buf, text, ap);
  if (directX == DXFULLSCREEN)
    ShowWindow (hWnd, SW_MINIMIZE);
  MessageBox (NULL, buf, "XaoS have problem", MB_OK | MB_ICONEXCLAMATION);
  va_end (ap);
}

void
x_fatalerror (const char *text, ...)
{
  va_list ap;
  char buf[4096];
  va_start (ap, text);
  vsprintf (buf, text, ap);
  if (directX == DXFULLSCREEN)
    ShowWindow (hWnd, SW_MINIMIZE);
  MessageBox (NULL, buf, "Unrecovable XaoS error", MB_OK | MB_ICONSTOP);
  va_end (ap);
  exit (1);
}


#endif /* WIN32_DRIVER */
