/*         ______   ___    ___ 
 *        /\  _  \ /\_ \  /\_ \ 
 *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___ 
 *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
 *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
 *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
 *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
 *                                           /\____/
 *                                           \_/__/
 *
 *      Windows OpenGL display driver
 *
 *      By Milan Mimica.
 *      Based on AllegroGL Windows display driver.
 *
 */

#include "allegro5/allegro5.h"
#include "allegro5/a5_opengl.h"
#include "allegro5/system_new.h"
#include "allegro5/internal/aintern.h"
#include "allegro5/internal/aintern_bitmap.h"
#include "allegro5/internal/aintern_memory.h"
#include "allegro5/internal/aintern_opengl.h"
#include "allegro5/internal/aintern_vector.h"
#include "allegro5/platform/aintwin.h"

#include "wgl.h"

#include <process.h>

ALLEGRO_DEBUG_CHANNEL("display")

static ALLEGRO_DISPLAY_INTERFACE *vt = 0;

/* Forward declarations: */
static void display_thread_proc(void *arg);
static void destroy_display_internals(ALLEGRO_DISPLAY_WGL *wgl_disp);

/* Prevents switching to desktop resolution when destroying the display. Used
   on full screen resize. */
static bool _wgl_do_not_change_display_mode = false;

/*
 * These parameters cannot be gotten by the display thread because
 * they're thread local. We get them in the calling thread first.
 */
typedef struct new_display_parameters {
   ALLEGRO_DISPLAY_WGL *display;
   volatile bool init_failed;
   HANDLE AckEvent;
} new_display_parameters;


static char* get_error_desc(DWORD err)
{
   #define MSGLEN 2048
   static char err_msg[MSGLEN];
   memset(err_msg, 0, MSGLEN);

   /* Get the formatting error string from Windows. Note that only the
    * bottom 14 bits matter - the rest are reserved for various library
    * IDs and type of error.
    */
   if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
                    | FORMAT_MESSAGE_IGNORE_INSERTS,
                      NULL, err & 0x3FFF,
                      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                     (LPTSTR) &err_msg, MSGLEN, NULL)) {
      strcpy(err_msg, "(Unable to decode the error code)");
   }
   else {
      /* Remove two trailing characters */
      if (strlen(err_msg) > 1)
         *(err_msg + strlen(err_msg) - 2) = '\0';
   }

   return err_msg;
}

/* Helper to set up GL state as we want it. */
static void setup_gl(ALLEGRO_DISPLAY *d)
{
   glViewport(0, 0, d->w, d->h);

   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   glOrtho(0, d->w, d->h, 0, -1, 1);

   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
}

static bool is_wgl_extension_supported(AL_CONST char *extension, HDC dc)
{
   ALLEGRO_GetExtensionsStringARB_t __wglGetExtensionsStringARB;
   int ret;

   if (!glGetString(GL_EXTENSIONS))
      return false;

   __wglGetExtensionsStringARB = (ALLEGRO_GetExtensionsStringARB_t)
                                  wglGetProcAddress("wglGetExtensionsStringARB");
   ret = _al_ogl_look_for_an_extension(extension,
         (const GLubyte*)__wglGetExtensionsStringARB(dc));

   return ret;
}


static HGLRC init_temp_context(HWND wnd)
{
   PIXELFORMATDESCRIPTOR pfd;
   int pf;
   HDC dc;
   HGLRC glrc;

   dc = GetDC(wnd);

   memset(&pfd, 0, sizeof(pfd));
   pfd.nSize = sizeof(pfd);
   pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL
	           | PFD_DOUBLEBUFFER_DONTCARE | PFD_STEREO_DONTCARE;
   pfd.iPixelType = PFD_TYPE_RGBA;
   pfd.iLayerType = PFD_MAIN_PLANE;
   pfd.cColorBits = 32;

   pf = ChoosePixelFormat(dc, &pfd);
   if (!pf) {
      ALLEGRO_ERROR("Unable to chose a temporary pixel format. %s\n",
                     get_error_desc(GetLastError()));
      return NULL;
   }

   memset(&pfd, 0, sizeof(pfd));
   if (!SetPixelFormat(dc, pf, &pfd)) {
      ALLEGRO_ERROR("Unable to set a temporary pixel format. %s\n",
                     get_error_desc(GetLastError()));
      return NULL;
   }

   glrc = wglCreateContext(dc);
   if (!glrc) {
      ALLEGRO_ERROR("Unable to create a render context. %s\n",
                     get_error_desc(GetLastError()));
      return NULL;
   }

   if (!wglMakeCurrent(dc, glrc)) {
      ALLEGRO_ERROR("Unable to set the render context as current. %s\n",
                     get_error_desc(GetLastError()));
      wglDeleteContext(glrc);
      return NULL;
   }

   return glrc;
}


static ALLEGRO_GetPixelFormatAttribivARB_t __wglGetPixelFormatAttribivARB = NULL;
static ALLEGRO_GetPixelFormatAttribivEXT_t __wglGetPixelFormatAttribivEXT = NULL;

static bool init_pixel_format_extensions(void)
{
   /* Load the ARB_p_f symbol - Note, we shouldn't use the extension
    * mechanism here, because it hasn't been initialized yet!
    */
   __wglGetPixelFormatAttribivARB =
      (ALLEGRO_GetPixelFormatAttribivARB_t)wglGetProcAddress("wglGetPixelFormatAttribivARB");
   __wglGetPixelFormatAttribivEXT = 
      (ALLEGRO_GetPixelFormatAttribivEXT_t)wglGetProcAddress("wglGetPixelFormatAttribivEXT");

   if (!__wglGetPixelFormatAttribivARB && !__wglGetPixelFormatAttribivEXT) {
      ALLEGRO_ERROR("WGL_ARB/EXT_pf not supported.\n");
      return false;
   }

   return true;
}


static ALLEGRO_CreateContextAttribsARB_t __wglCreateContextAttribsARB = NULL;

static bool init_context_creation_extensions(void)
{
   __wglCreateContextAttribsARB =
      (ALLEGRO_CreateContextAttribsARB_t)wglGetProcAddress("wglCreateContextAttribsARB");

   if (!__wglCreateContextAttribsARB) {
      ALLEGRO_ERROR("wglCreateContextAttribs not supported!\n");
      return false;
   }

   return true;
}


static int get_pixel_formats_count_old(HDC dc)
{
   PIXELFORMATDESCRIPTOR pfd;
   int ret;

   ret = DescribePixelFormat(dc, 1, sizeof(pfd), &pfd);
   if (!ret) {
      ALLEGRO_ERROR("DescribePixelFormat failed! %s\n",
                     get_error_desc(GetLastError()));
   }

   return ret;
}


static int get_pixel_formats_count_ext(HDC dc)
{
   int attrib[1];
   int value[1];

   attrib[0] = WGL_NUMBER_PIXEL_FORMATS_ARB;
   if ((__wglGetPixelFormatAttribivARB(dc, 0, 0, 1, attrib, value) == GL_FALSE)
    && (__wglGetPixelFormatAttribivEXT(dc, 0, 0, 1, attrib, value) == GL_FALSE)) {
        ALLEGRO_ERROR("WGL_ARB/EXT_pixel_format use failed! %s\n",
                       get_error_desc(GetLastError()));
   }

   return value[0];
}


static void display_pixel_format(ALLEGRO_EXTRA_DISPLAY_SETTINGS *eds)
{
   ALLEGRO_INFO("Accelarated: %s\n", eds->settings[ALLEGRO_RENDER_METHOD] ? "yes" : "no");
   ALLEGRO_INFO("Single-buffer: %s\n", eds->settings[ALLEGRO_SINGLE_BUFFER] ? "yes" : "no");
   if (eds->settings[ALLEGRO_SWAP_METHOD] > 0)
      ALLEGRO_INFO("Swap method: %s\n", eds->settings[ALLEGRO_SWAP_METHOD] == 2 ? "flip" : "copy");
   else
      ALLEGRO_INFO("Swap method: undefined\n");
   ALLEGRO_INFO("Color format: r%i g%i b%i a%i, %i bit\n",
      eds->settings[ALLEGRO_RED_SIZE],
      eds->settings[ALLEGRO_GREEN_SIZE],
      eds->settings[ALLEGRO_BLUE_SIZE],
      eds->settings[ALLEGRO_ALPHA_SIZE],
      eds->settings[ALLEGRO_COLOR_SIZE]);
   ALLEGRO_INFO("Depth buffer: %i bits\n", eds->settings[ALLEGRO_DEPTH_SIZE]);
   ALLEGRO_INFO("Sample buffers: %s\n", eds->settings[ALLEGRO_SAMPLE_BUFFERS] ? "yes" : "no");
   ALLEGRO_INFO("Samples: %i\n", eds->settings[ALLEGRO_SAMPLES]);
}


static int decode_pixel_format_old(PIXELFORMATDESCRIPTOR *pfd,
                                   ALLEGRO_EXTRA_DISPLAY_SETTINGS *eds)
{
   ALLEGRO_INFO("Decoding:\n");

	/* Not interested if it doesn't support OpenGL and RGBA */
   if (!(pfd->dwFlags & PFD_SUPPORT_OPENGL)) {
      ALLEGRO_INFO("OpenGL Unsupported\n");
      return false;
   }
   if (pfd->iPixelType != PFD_TYPE_RGBA) {
      ALLEGRO_INFO("Not RGBA mode\n");
      return false;
   }

   /* hardware acceleration */
   if (((pfd->dwFlags & PFD_GENERIC_ACCELERATED) && (pfd->dwFlags & PFD_GENERIC_FORMAT))
   || (!(pfd->dwFlags & PFD_GENERIC_ACCELERATED) && !(pfd->dwFlags & PFD_GENERIC_FORMAT)))
      eds->settings[ALLEGRO_RENDER_METHOD] = 1;
   else
      eds->settings[ALLEGRO_RENDER_METHOD] = 0;

   /* Depths of colour buffers */
   eds->settings[ALLEGRO_RED_SIZE] = pfd->cRedBits;
   eds->settings[ALLEGRO_GREEN_SIZE] = pfd->cGreenBits;
   eds->settings[ALLEGRO_BLUE_SIZE] = pfd->cBlueBits;
   eds->settings[ALLEGRO_ALPHA_SIZE] = pfd->cAlphaBits;

   /* Depths of accumulation buffer */
   eds->settings[ALLEGRO_ACC_RED_SIZE] = pfd->cAccumRedBits;
   eds->settings[ALLEGRO_ACC_GREEN_SIZE] = pfd->cAccumGreenBits;
   eds->settings[ALLEGRO_ACC_BLUE_SIZE] = pfd->cAccumBlueBits;
   eds->settings[ALLEGRO_ACC_ALPHA_SIZE] = pfd->cAccumAlphaBits;

   /* Miscellaneous settings */
   eds->settings[ALLEGRO_SINGLE_BUFFER] = !(pfd->dwFlags & PFD_DOUBLEBUFFER);
   eds->settings[ALLEGRO_DEPTH_SIZE] = pfd->cDepthBits;
   eds->settings[ALLEGRO_STENCIL_SIZE] = pfd->cStencilBits;
   eds->settings[ALLEGRO_COLOR_SIZE] = pfd->cColorBits;
   eds->settings[ALLEGRO_STEREO] = pfd->dwFlags & PFD_STEREO;
   eds->settings[ALLEGRO_AUX_BUFFERS] = pfd->cAuxBuffers;

   /* These are the component shifts. */
   eds->settings[ALLEGRO_RED_SHIFT] = pfd->cRedShift;
   eds->settings[ALLEGRO_GREEN_SHIFT] = pfd->cGreenShift;
   eds->settings[ALLEGRO_BLUE_SHIFT] = pfd->cBlueShift;
   eds->settings[ALLEGRO_ALPHA_SHIFT] = pfd->cAlphaShift;

   /* Multisampling isn't supported under Windows if we don't also use
    * WGL_ARB_pixel_format or WGL_EXT_pixel_format.
    */
   eds->settings[ALLEGRO_SAMPLE_BUFFERS] = 0;
   eds->settings[ALLEGRO_SAMPLES] = 0;

   /* Swap method can't be detected without WGL_ARB_pixel_format or
    * WGL_EXT_pixel_format
    */
   eds->settings[ALLEGRO_SWAP_METHOD] = 0;

   /* Float depth/color isn't supported under Windows if we don't also use
    * AGL_ARB_pixel_format or WGL_EXT_pixel_format.
    */
   eds->settings[ALLEGRO_FLOAT_COLOR] = 0;
   eds->settings[ALLEGRO_FLOAT_DEPTH] = 0;

   eds->settings[ALLEGRO_COMPATIBLE_DISPLAY] = 1;

	return true;
}


static bool decode_pixel_format_attrib(ALLEGRO_EXTRA_DISPLAY_SETTINGS *eds, int num_attribs,
                                      const int *attrib, const int *value)
{
   int i;

   ALLEGRO_INFO("Decoding:\n");

   eds->settings[ALLEGRO_SAMPLES] = 0;
   eds->settings[ALLEGRO_SAMPLE_BUFFERS] = 0;
   eds->settings[ALLEGRO_FLOAT_DEPTH] = 0;
   eds->settings[ALLEGRO_FLOAT_COLOR] = 0;
   eds->settings[ALLEGRO_COMPATIBLE_DISPLAY] = 1;

   for (i = 0; i < num_attribs; i++) {
      /* Not interested if it doesn't support OpenGL or window drawing or RGBA. */
      if (attrib[i] == WGL_SUPPORT_OPENGL_ARB && value[i] == 0) {	
         ALLEGRO_INFO("OpenGL Unsupported\n");
         return false;
      }
      else if (attrib[i] == WGL_DRAW_TO_WINDOW_ARB && value[i] == 0) {	
         ALLEGRO_INFO("Can't draw to window\n");
         return false;
      }
      else if (attrib[i] == WGL_PIXEL_TYPE_ARB
         && (value[i] != WGL_TYPE_RGBA_ARB && value[i] != WGL_TYPE_RGBA_FLOAT_ARB)) {
         ALLEGRO_INFO("Not RGBA mode\n");
         return false;
      }
      /* hardware acceleration */
      else if (attrib[i] == WGL_ACCELERATION_ARB) {
         eds->settings[ALLEGRO_RENDER_METHOD] = (value[i] == WGL_NO_ACCELERATION_ARB) ? 0 : 1;
      }
      /* Depths of colour buffers */
      else if (attrib[i] == WGL_RED_BITS_ARB) {
         eds->settings[ALLEGRO_RED_SIZE] = value[i];
      }
      else if (attrib[i] == WGL_GREEN_BITS_ARB) {
         eds->settings[ALLEGRO_GREEN_SIZE] = value[i];
      }
      else if (attrib[i] == WGL_BLUE_BITS_ARB) {
         eds->settings[ALLEGRO_BLUE_SIZE] = value[i];
      }
      else if (attrib[i] == WGL_ALPHA_BITS_ARB) {
         eds->settings[ALLEGRO_ALPHA_SIZE] = value[i];
      }
      /* Shift of color components */
      else if (attrib[i] == WGL_RED_SHIFT_ARB) {
         eds->settings[ALLEGRO_RED_SHIFT] = value[i];
      }
      else if (attrib[i] == WGL_GREEN_SHIFT_ARB) {
         eds->settings[ALLEGRO_GREEN_SHIFT] = value[i];
      }
      else if (attrib[i] == WGL_BLUE_SHIFT_ARB) {
         eds->settings[ALLEGRO_BLUE_SHIFT] = value[i];
      }
      else if (attrib[i] == WGL_ALPHA_SHIFT_ARB) {
         eds->settings[ALLEGRO_ALPHA_SHIFT] = value[i];
      }
      /* Miscellaneous settings */
      else if (attrib[i] == WGL_DOUBLE_BUFFER_ARB) {
         eds->settings[ALLEGRO_SINGLE_BUFFER] = !(value[i]);
      }
      else if (attrib[i] == WGL_SWAP_METHOD_ARB) {
         if (value[i] == WGL_SWAP_UNDEFINED_ARB)
            eds->settings[ALLEGRO_SWAP_METHOD] = 0;
         else if (value[i] == WGL_SWAP_COPY_ARB)
            eds->settings[ALLEGRO_SWAP_METHOD] = 1;
         else if (value[i] == WGL_SWAP_EXCHANGE_ARB) 
            eds->settings[ALLEGRO_SWAP_METHOD] = 2;
      }

      else if (attrib[i] == WGL_STEREO_ARB) {
         eds->settings[ALLEGRO_STEREO] = value[i];
      }
      else if (attrib[i] == WGL_AUX_BUFFERS_ARB) {
         eds->settings[ALLEGRO_AUX_BUFFERS] = value[i];
      }
      else if (attrib[i] == WGL_STENCIL_BITS_ARB) {
         eds->settings[ALLEGRO_STENCIL_SIZE] = value[i];
      }
      /* Depths of accumulation buffer */
      else if (attrib[i] == WGL_ACCUM_RED_BITS_ARB) {
         eds->settings[ALLEGRO_ACC_RED_SIZE] = value[i];
      }
      else if (attrib[i] == WGL_ACCUM_GREEN_BITS_ARB) {
         eds->settings[ALLEGRO_ACC_GREEN_SIZE] = value[i];
      }
      else if (attrib[i] == WGL_ACCUM_BLUE_BITS_ARB) {
         eds->settings[ALLEGRO_ACC_BLUE_SIZE] = value[i];
      }
      else if (attrib[i] == WGL_ACCUM_ALPHA_BITS_ARB) {
         eds->settings[ALLEGRO_ACC_ALPHA_SIZE] = value[i];
      }

      else if (attrib[i] == WGL_DEPTH_BITS_ARB) {
         eds->settings[ALLEGRO_DEPTH_SIZE] = value[i];
      }
      else if (attrib[i] == WGL_COLOR_BITS_ARB) {
         eds->settings[ALLEGRO_COLOR_SIZE] = value[i];
      }
      /* Multisampling bits */
      else if (attrib[i] == WGL_SAMPLE_BUFFERS_ARB) {
         eds->settings[ALLEGRO_SAMPLE_BUFFERS] = value[i];
      }
      else if (attrib[i] == WGL_SAMPLES_ARB) {
         eds->settings[ALLEGRO_SAMPLES] = value[i];
      }
      /* Float color */
      if (attrib[i] == WGL_PIXEL_TYPE_ARB && value[i] == WGL_TYPE_RGBA_FLOAT_ARB) {
         eds->settings[ALLEGRO_FLOAT_COLOR] = true;
      }
      /* Float depth */
      else if (attrib[i] == WGL_DEPTH_FLOAT_EXT) {
         eds->settings[ALLEGRO_FLOAT_DEPTH] = value[i];
      }
   }

   return true;
}


static ALLEGRO_EXTRA_DISPLAY_SETTINGS* read_pixel_format_old(int fmt, HDC dc)
{
   ALLEGRO_EXTRA_DISPLAY_SETTINGS *eds = NULL;
   PIXELFORMATDESCRIPTOR pfd;
   int result;

   result = DescribePixelFormat(dc, fmt+1, sizeof(pfd), &pfd);
   if (!result) {
      ALLEGRO_WARN("DescribePixelFormat() failed. %s\n",
                    get_error_desc(GetLastError()));
      return NULL;
   }

   eds = malloc(sizeof *eds);
   memset(eds, 0, sizeof *eds);
   if (!decode_pixel_format_old(&pfd, eds)) {
      free(eds);
      return NULL;
   }

   return eds;
}


static ALLEGRO_EXTRA_DISPLAY_SETTINGS* read_pixel_format_ext(int fmt, HDC dc)
{
   ALLEGRO_EXTRA_DISPLAY_SETTINGS *eds = NULL;

   /* Note: Even though we use te ARB suffix, all those enums are compatible
    * with EXT_pixel_format.
    */
   int attrib[] = {
      WGL_SUPPORT_OPENGL_ARB,
      WGL_DRAW_TO_WINDOW_ARB,
      WGL_PIXEL_TYPE_ARB,
      WGL_ACCELERATION_ARB,
      WGL_DOUBLE_BUFFER_ARB,
      WGL_DEPTH_BITS_ARB,
      WGL_SWAP_METHOD_ARB,
      WGL_COLOR_BITS_ARB,
      WGL_RED_BITS_ARB,
      WGL_GREEN_BITS_ARB,
      WGL_BLUE_BITS_ARB,
      WGL_ALPHA_BITS_ARB,
      WGL_RED_SHIFT_ARB,
      WGL_GREEN_SHIFT_ARB,
      WGL_BLUE_SHIFT_ARB,
      WGL_ALPHA_SHIFT_ARB,
      WGL_STENCIL_BITS_ARB,
      WGL_STEREO_ARB,
      WGL_ACCUM_BITS_ARB,
      WGL_ACCUM_RED_BITS_ARB,
      WGL_ACCUM_GREEN_BITS_ARB,
      WGL_ACCUM_BLUE_BITS_ARB,
      WGL_ACCUM_ALPHA_BITS_ARB,
      WGL_AUX_BUFFERS_ARB,

      /* The following are used by extensions that add to WGL_pixel_format.
       * If WGL_p_f isn't supported though, we can't use the (then invalid)
       * enums. We can't use any magic number either, so we settle for 
       * replicating one. The pixel format decoder
       * (decode_pixel_format_attrib()) doesn't care about duplicates.
       */
      WGL_AUX_BUFFERS_ARB, /* placeholder for WGL_SAMPLE_BUFFERS_ARB */
      WGL_AUX_BUFFERS_ARB, /* placeholder for WGL_SAMPLES_ARB        */
      WGL_AUX_BUFFERS_ARB, /* placeholder for WGL_DEPTH_FLOAT_EXT    */
   };

   const int num_attribs = sizeof(attrib) / sizeof(attrib[0]);
   int *value = (int*)malloc(sizeof(int) * num_attribs);
   int ret;

   if (!value)
      return NULL;

   /* If multisampling is supported, query for it. */
   if (is_wgl_extension_supported("WGL_ARB_multisample", dc)) {
      attrib[num_attribs - 3] = WGL_SAMPLE_BUFFERS_ARB;
      attrib[num_attribs - 2] = WGL_SAMPLES_ARB;
   }
   if (is_wgl_extension_supported("WGL_EXT_depth_float", dc)) {
      attrib[num_attribs - 1] = WGL_DEPTH_FLOAT_EXT;
   }

   /* Get the pf attributes */
   if (__wglGetPixelFormatAttribivARB) {
      ret = __wglGetPixelFormatAttribivARB(dc, fmt+1, 0, num_attribs, attrib, value);
   }
   else if (__wglGetPixelFormatAttribivEXT) {
      ret = __wglGetPixelFormatAttribivEXT(dc, fmt+1, 0, num_attribs, attrib, value);
   }
   else {
      ret = 0;
   }
   
   if (!ret) {
      ALLEGRO_ERROR("wglGetPixelFormatAttrib failed! %s\n",
                     get_error_desc(GetLastError()));
      free(value);
      return NULL;
   }

   eds = malloc(sizeof *eds);
   memset(eds, 0, sizeof *eds);
   if (!decode_pixel_format_attrib(eds, num_attribs, attrib, value)) {
      free(eds);
      eds = NULL;
   }

   free(value);

   return eds;
}


static bool change_display_mode(ALLEGRO_DISPLAY *d)
{
   DEVMODE dm;
   DEVMODE fallback_dm;
   DISPLAY_DEVICE dd;
   char* dev_name = NULL;
   int i, modeswitch, result;
   int fallback_dm_valid = 0;
   int bpp;
   int adapter = al_get_current_video_adapter();

   if (adapter != -1) {
      memset(&dd, 0, sizeof(dd));
      dd.cb = sizeof(dd);
      if (EnumDisplayDevices(NULL, adapter, &dd, 0) == false)
         return false;
      dev_name = dd.DeviceName;
   }

   memset(&fallback_dm, 0, sizeof(fallback_dm));
   memset(&dm, 0, sizeof(dm));
   dm.dmSize = sizeof(DEVMODE);

   bpp = d->extra_settings.settings[ALLEGRO_COLOR_SIZE];
   if (!bpp)
      bpp = 32;

   i = 0;
   do {
      modeswitch = EnumDisplaySettings(dev_name, i, &dm);
      if (!modeswitch)
         break;

      if ((dm.dmPelsWidth  == (unsigned) d->w)
       && (dm.dmPelsHeight == (unsigned) d->h)
       && (dm.dmBitsPerPel == (unsigned) bpp)
       && (dm.dmDisplayFrequency != (unsigned) d->refresh_rate)) {
         /* Keep it as fallback if refresh rate request could not
          * be satisfied. Try to get as close to 60Hz as possible though,
          * it's a bit better for a fallback than just blindly picking
          * something like 47Hz or 200Hz.
          */
         if (!fallback_dm_valid) {
            fallback_dm = dm;
            fallback_dm_valid = 1;
         }
         else if (dm.dmDisplayFrequency >= 60) {
            if (dm.dmDisplayFrequency < fallback_dm.dmDisplayFrequency) {
                fallback_dm = dm;
            }
         }
      }
      i++;
   }
   while ((dm.dmPelsWidth  != (unsigned) d->w)
       || (dm.dmPelsHeight != (unsigned) d->h)
       || (dm.dmBitsPerPel != (unsigned) bpp)
       || (dm.dmDisplayFrequency != (unsigned) d->refresh_rate));

   if (!modeswitch && !fallback_dm_valid) {
      ALLEGRO_ERROR("Mode not found.\n");
      return false;
   }

   if (!modeswitch && fallback_dm_valid)
      dm = fallback_dm;

   dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
   result = ChangeDisplaySettingsEx(dev_name, &dm, NULL, CDS_FULLSCREEN, 0);

   if (result != DISP_CHANGE_SUCCESSFUL) {
      ALLEGRO_ERROR("Unable to set mode. %s\n",
                     get_error_desc(GetLastError()));
      return false;
   }

   ALLEGRO_INFO("Mode seccessfuly set.\n");
   return true;
}


static HGLRC init_ogl_context_ex(HDC dc, int fc, int major, int minor)
{
   HWND testwnd = NULL;
   HDC testdc   = NULL;
   HGLRC testrc = NULL;
   HGLRC old_rc = NULL;
   HDC old_dc   = NULL;
   HGLRC glrc   = NULL;

   testwnd = _al_win_create_hidden_window();
   if (!testwnd)
      return NULL;

   old_rc = wglGetCurrentContext();
   old_dc = wglGetCurrentDC();

   testdc = GetDC(testwnd);
   testrc = init_temp_context(testwnd);
   if (!testrc)
      goto bail;

   if (is_wgl_extension_supported("WGL_ARB_create_context", testdc)) {
      int attrib[] = {WGL_CONTEXT_MAJOR_VERSION_ARB, major,
                      WGL_CONTEXT_MINOR_VERSION_ARB, minor,
                      WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, fc,
                      0};
      if (!init_context_creation_extensions())
         goto bail;
      /* TODO: we could use the context sharing feature */
      glrc = __wglCreateContextAttribsARB(dc, 0, attrib);
   }
   else
      goto bail;

bail:
   wglMakeCurrent(NULL, NULL);
   if (testrc) {
      wglDeleteContext(testrc);
   }

   wglMakeCurrent(old_dc, old_rc);

   __wglCreateContextAttribsARB = NULL;

   if (testwnd) {
      ReleaseDC(testwnd, testdc);
      DestroyWindow(testwnd);
   }

   return glrc;
}


static ALLEGRO_EXTRA_DISPLAY_SETTINGS** get_available_pixel_formats_ext(int *count)
{
   HWND testwnd = NULL;
   HDC testdc   = NULL;
   HGLRC testrc = NULL;
   HGLRC old_rc = NULL;
   HDC old_dc   = NULL;
   ALLEGRO_EXTRA_DISPLAY_SETTINGS **eds_list = NULL;
   ALLEGRO_EXTRA_DISPLAY_SETTINGS *ref;
   int maxindex;
   int i, j;

   *count = 0;
   ref =  _al_get_new_display_settings();

   /* We need to create a dummy window with a pixel format to get the
    * list of valid PFDs
    */
   testwnd = _al_win_create_hidden_window();
   if (!testwnd)
      return false;

   old_rc = wglGetCurrentContext();
   old_dc = wglGetCurrentDC();

   testdc = GetDC(testwnd);
   testrc = init_temp_context(testwnd);
   if (!testrc)
      goto bail;

  if (!is_wgl_extension_supported("WGL_ARB_pixel_format", testdc) &&
      !is_wgl_extension_supported("WGL_EXT_pixel_format", testdc)) {
      ALLEGRO_ERROR("WGL_ARB/EXT_pf not supported.\n");
      goto bail;
  }

   if (!init_pixel_format_extensions())
      goto bail;

   maxindex = get_pixel_formats_count_ext(testdc);
   if (maxindex < 1)
      goto bail;

   ALLEGRO_INFO("Got %i visuals.\n", maxindex);

   eds_list = malloc(maxindex * sizeof(*eds_list));
   if (!eds_list)
      goto bail;
   memset(eds_list, 0, sizeof *eds_list);

   for (j = i = 0; i < maxindex; i++) {
      ALLEGRO_INFO("-- \n");
      ALLEGRO_INFO("Decoding visual no. %i...\n", i+1);
      eds_list[j] = read_pixel_format_ext(i, testdc);
      if (!eds_list[j])
         continue;
      display_pixel_format(eds_list[j]);
      eds_list[j]->score = _al_score_display_settings(eds_list[j], ref);
      if (eds_list[j]->score == -1) {
         free(eds_list[j]);
         eds_list[j] = NULL;
         continue;
      }
      /* In WinAPI first index is 1 ::) */
      eds_list[j]->index = i+1;
      j++;
   }

   ALLEGRO_INFO("%i visuals are good enough.\n", j);
   *count = j;

bail:
   wglMakeCurrent(NULL, NULL);
   if (testrc) {
      wglDeleteContext(testrc);
   }

   wglMakeCurrent(old_dc, old_rc);

   __wglGetPixelFormatAttribivARB = NULL;
   __wglGetPixelFormatAttribivEXT = NULL;

   if (testwnd) {
      ReleaseDC(testwnd, testdc);
      DestroyWindow(testwnd);
   }

   return eds_list;
}


static ALLEGRO_EXTRA_DISPLAY_SETTINGS** get_available_pixel_formats_old(int *count, HDC dc)
{
   ALLEGRO_EXTRA_DISPLAY_SETTINGS **eds_list = NULL;
   ALLEGRO_EXTRA_DISPLAY_SETTINGS *ref;
   int maxindex;
   int i, j;

   *count = 0;
   ref =  _al_get_new_display_settings();

   maxindex = get_pixel_formats_count_old(dc);
   if (maxindex < 1)
      return NULL;

   ALLEGRO_INFO("Got %i visuals.\n", maxindex);

   eds_list = malloc(maxindex * sizeof(*eds_list));
   if (!eds_list)
      return NULL;
   memset(eds_list, 0, sizeof *eds_list);

   for (j = i = 0; i < maxindex; i++) {
      ALLEGRO_INFO("-- \n");
      ALLEGRO_INFO("Decoding visual no. %i...\n", i+1);
      eds_list[j] = read_pixel_format_old(i, dc);
      if (!eds_list[j])
         continue;
#ifdef DEBUGMODE
      display_pixel_format(eds_list[j]);
#endif
      eds_list[j]->score = _al_score_display_settings(eds_list[j], ref);
      if (eds_list[j]->score == -1) {
         free(eds_list[j]);
         eds_list[j] = NULL;
         continue;
      }
      /* In WinAPI first index is 1 ::) */
      eds_list[j]->index = i+1;
      j++;
   }

   ALLEGRO_INFO("%i visuals are good enough.\n", j);
   *count = j;

   return eds_list;
}


static bool select_pixel_format(ALLEGRO_DISPLAY_WGL *d, HDC dc)
{
   ALLEGRO_EXTRA_DISPLAY_SETTINGS **eds = NULL;
   ALLEGRO_SYSTEM *system = (void *)al_system_driver();
   int eds_count = 0;
   int i;
   bool force_old = false;

   if (system->config) {
      const char *selection_mode;
      selection_mode = al_get_config_value(system->config, "graphics",
                          "config_selection");
      if (selection_mode && selection_mode[0] != '\0') {
         if (!stricmp(selection_mode, "old")) {
            ALLEGRO_INFO("Forcing OLD visual selection method.\n");
            force_old = true;
         }
         else if (!stricmp(selection_mode, "new"))
            force_old = false;
      }
   }

   if (!force_old)
      eds = get_available_pixel_formats_ext(&eds_count);
   if (!eds)
      eds = get_available_pixel_formats_old(&eds_count, dc);

   if (!eds || !eds_count) {
      ALLEGRO_ERROR("Didn't find any suitable pixel format!\n");
      return false;
   }

   qsort(eds, eds_count, sizeof(eds), _al_display_settings_sorter);

   for (i = 0; i < eds_count ; i++) {
      if (SetPixelFormat(d->dc, eds[i]->index, NULL)) {
         ALLEGRO_INFO("Chose visual no. %i\n\n", eds[i]->index);
         display_pixel_format(eds[i]);
         break;
      }
      else {
         ALLEGRO_WARN("Unable to set pixel format! %s\n",
                       get_error_desc(GetLastError()));
         ALLEGRO_WARN("Trying next one.\n");
      }
   }

   if (i == eds_count) {
      ALLEGRO_ERROR("Unable to set any pixel format! %s\n",
                     get_error_desc(GetLastError()));
      for (i = 0; i < eds_count; i++)
         free(eds[i]);
      free(eds);
      return false;
   }

   memcpy(&d->win_display.display.extra_settings, eds[i], sizeof(ALLEGRO_EXTRA_DISPLAY_SETTINGS));

   for (i = 0; i < eds_count; i++)
      free(eds[i]);
   if (eds)
      free(eds);

   return true;
}


static bool create_display_internals(ALLEGRO_DISPLAY_WGL *wgl_disp)
{
   ALLEGRO_DISPLAY     *disp     = (void*)wgl_disp;
   ALLEGRO_DISPLAY_WIN *win_disp = (void*)wgl_disp;
   new_display_parameters ndp;
   HANDLE window_thread;

   ndp.display = wgl_disp;
   ndp.init_failed = true;
   ndp.AckEvent = CreateEvent(NULL, false, false, NULL);
   window_thread = (HANDLE)_beginthread(display_thread_proc, 0, &ndp);

   /* Wait some _finite_ time (10 secs or so) for display thread to init, and
    * give up if something horrible happened to it, unless we're in debug mode
    * and we may have intentionally stopped the execution to analyze the code.
    */
#ifdef DEBUGMODE
   WaitForSingleObject(ndp.AckEvent, INFINITE);
#else
   WaitForSingleObject(ndp.AckEvent, 10*1000);
#endif

   CloseHandle(ndp.AckEvent);

   if (ndp.init_failed) {
      ALLEGRO_ERROR("Failed to create display.\n");
      return false;
   }

   /* WGL display lists cannot be shared with the API currently in use. */
   disp->ogl_extras->is_shared = false;
   
   if (!select_pixel_format(wgl_disp, wgl_disp->dc)) {
      destroy_display_internals(wgl_disp);
      return false;
   }

   if (disp->flags & ALLEGRO_OPENGL_3_0) {
      int fc = disp->flags & ALLEGRO_OPENGL_FORWARD_COMPATIBLE;
      wgl_disp->glrc = init_ogl_context_ex(wgl_disp->dc, fc, 3, 0);
   }
   else {
      wgl_disp->glrc = wglCreateContext(wgl_disp->dc);
   }

   if (!wgl_disp->glrc) {
      ALLEGRO_ERROR("Unable to create a render context! %s\n",
                     get_error_desc(GetLastError()));
      destroy_display_internals(wgl_disp);
      return false;
   }

   /* make the context the current one */
   if (!wglMakeCurrent(wgl_disp->dc, wgl_disp->glrc)) {
      ALLEGRO_ERROR("Unable to make the context current! %s\n",
                     get_error_desc(GetLastError()));
      destroy_display_internals(wgl_disp);
      return false;
   }

   _al_ogl_manage_extensions(disp);
   _al_ogl_set_extensions(disp->ogl_extras->extension_api);

   if (disp->ogl_extras->ogl_info.version < 1.2) {
      ALLEGRO_EXTRA_DISPLAY_SETTINGS *eds = _al_get_new_display_settings();
      if (eds->required & (1<<ALLEGRO_COMPATIBLE_DISPLAY)) {
         ALLEGRO_WARN("Allegro requires at least OpenGL version 1.2 to work.\n");
         destroy_display_internals(wgl_disp);
         return false;
      }
      disp->extra_settings.settings[ALLEGRO_COMPATIBLE_DISPLAY] = 0;
   }
   else if (disp->ogl_extras->ogl_info.version > 2.1) {
      /* We don't have OpenGL3 a driver. */
      disp->extra_settings.settings[ALLEGRO_COMPATIBLE_DISPLAY] = 0;
   }

   disp->ogl_extras->backbuffer = _al_ogl_create_backbuffer(disp);
   if (!disp->ogl_extras->backbuffer) {
      ALLEGRO_ERROR("Failed to create a backbuffer.\n");
      destroy_display_internals(wgl_disp);
      return false;
   }
 
   win_disp->mouse_range_x1 = 0;
   win_disp->mouse_range_y1 = 0;
   win_disp->mouse_range_x2 = disp->w;
   win_disp->mouse_range_y2 = disp->h;
   /* XXX: These calls use the current display (which is not set to this
           display yet). Mouse routines semantics have to be sorted out first.
   if (al_is_mouse_installed()) {
      al_set_mouse_xy(disp->w/2, disp->h/2);
      al_set_mouse_range(0, 0, disp->w, disp->h);
   }*/

   win_disp->mouse_selected_hcursor = 0;
   win_disp->mouse_cursor_shown = false;

   _al_win_grab_input(win_disp);

   if (disp->extra_settings.settings[ALLEGRO_COMPATIBLE_DISPLAY])
      setup_gl(disp);

   return true;
}


static ALLEGRO_DISPLAY* wgl_create_display(int w, int h)
{
   ALLEGRO_SYSTEM_WIN *system = (ALLEGRO_SYSTEM_WIN *)al_system_driver();
   ALLEGRO_DISPLAY_WGL **add;
   ALLEGRO_DISPLAY_WGL *wgl_display = _AL_MALLOC(sizeof *wgl_display);
   ALLEGRO_DISPLAY *ogl_display = (void*)wgl_display;
   ALLEGRO_DISPLAY     *display     = (void*)ogl_display;

   memset(display, 0, sizeof *wgl_display);
   display->w = w;
   display->h = h;
   display->refresh_rate = al_get_new_display_refresh_rate();
   display->flags = al_get_new_display_flags();
   display->vt = vt;

   display->ogl_extras = _AL_MALLOC(sizeof(ALLEGRO_OGL_EXTRAS));
   memset(display->ogl_extras, 0, sizeof(ALLEGRO_OGL_EXTRAS));

   if (!create_display_internals(wgl_display)) {
      _AL_FREE(display->ogl_extras);
      _AL_FREE(display);
      return NULL;
   }

   /* Print out OpenGL version info */
   ALLEGRO_INFO("OpenGL Version: %s\n", (const char*)glGetString(GL_VERSION));
   ALLEGRO_INFO("Vendor: %s\n", (const char*)glGetString(GL_VENDOR));
   ALLEGRO_INFO("Renderer: %s\n\n", (const char*)glGetString(GL_RENDERER));

   /* Add ourself to the list of displays. */
   add = _al_vector_alloc_back(&system->system.displays);
   *add = wgl_display;

   /* Each display is an event source. */
   _al_event_source_init(&display->es);

   _al_win_set_system_mouse_cursor(display, ALLEGRO_SYSTEM_MOUSE_CURSOR_ARROW);
   _al_win_show_mouse_cursor(display);

   return display;
}


static void destroy_display_internals(ALLEGRO_DISPLAY_WGL *wgl_disp)
{
   ALLEGRO_DISPLAY *disp = (ALLEGRO_DISPLAY *)wgl_disp;
   ALLEGRO_DISPLAY_WIN *win_disp = (ALLEGRO_DISPLAY_WIN *)wgl_disp;

   /* We need to convert all our bitmaps to display independent (memory)
    * bitmaps because WGL driver doesn't support sharing of resources. */
   while (disp->bitmaps._size > 0) {
      ALLEGRO_BITMAP **bptr = _al_vector_ref_back(&disp->bitmaps);
      ALLEGRO_BITMAP *bmp = *bptr;
      _al_convert_to_memory_bitmap(bmp);
   }

   if (disp->ogl_extras->backbuffer)
      _al_ogl_destroy_backbuffer(disp->ogl_extras->backbuffer);
   disp->ogl_extras->backbuffer = NULL;

   _al_ogl_unmanage_extensions(disp);

   PostMessage(win_disp->window, _al_win_msg_suicide, (WPARAM)win_disp, 0);
   while (!win_disp->thread_ended)
      al_rest(0.001);
}


static void wgl_destroy_display(ALLEGRO_DISPLAY *disp)
{
   ALLEGRO_SYSTEM_WIN *system = (ALLEGRO_SYSTEM_WIN *)al_system_driver();
   ALLEGRO_DISPLAY_WGL *wgl_disp = (ALLEGRO_DISPLAY_WGL *)disp;
   ALLEGRO_DISPLAY *old_disp = al_get_current_display();

   if (old_disp != disp)
      al_set_current_display(disp);

   destroy_display_internals(wgl_disp);
   _al_event_source_free(&disp->es);
   _al_vector_find_and_delete(&system->system.displays, &disp);

   _al_vector_free(&disp->bitmaps);
   _AL_FREE(disp->ogl_extras);

   if (old_disp != disp)
      al_set_current_display(old_disp);

   _AL_FREE(wgl_disp);
}


static bool wgl_set_current_display(ALLEGRO_DISPLAY *d)
{
   ALLEGRO_DISPLAY_WGL *wgl_disp = (ALLEGRO_DISPLAY_WGL *)d;
   ALLEGRO_DISPLAY *ogl_disp = (ALLEGRO_DISPLAY *)d;
   HGLRC current_glrc;

   current_glrc = wglGetCurrentContext();

   if (current_glrc && current_glrc != wgl_disp->glrc) {
      /* make the context the current one */
      if (!wglMakeCurrent(wgl_disp->dc, wgl_disp->glrc)) {
         ALLEGRO_ERROR("Unable to make the context current! %s\n",
                        get_error_desc(GetLastError()));
         return false;
      }

      _al_ogl_set_extensions(ogl_disp->ogl_extras->extension_api);
   }

   return true;
}


/*
 * The window must be created in the same thread that
 * runs the message loop.
 */
static void display_thread_proc(void *arg)
{
   new_display_parameters *ndp = arg;
   ALLEGRO_DISPLAY *disp = (ALLEGRO_DISPLAY*)ndp->display;
   ALLEGRO_DISPLAY_WGL *wgl_disp = (void*)disp;
   ALLEGRO_DISPLAY_WIN *win_disp = (void*)disp;
   MSG msg;

   /* So that we can call the functions using TLS from this thread. */
   al_set_new_display_flags(disp->flags);
   
   if (disp->flags & ALLEGRO_FULLSCREEN) {
      if (!change_display_mode(disp)) {
         win_disp->thread_ended = true;
         destroy_display_internals(wgl_disp);
         SetEvent(ndp->AckEvent);
         return;
      }
   }

   win_disp->window = _al_win_create_window(disp, disp->w, disp->h, disp->flags);

   if (!win_disp->window) {
      win_disp->thread_ended = true;
      destroy_display_internals(wgl_disp);
      SetEvent(ndp->AckEvent);
      return;
   }

   /* FIXME: can't _al_win_create_window() do this? */
   if (disp->flags & ALLEGRO_FULLSCREEN) {
      RECT rect;
      rect.left = 0;
      rect.right = disp->w;
      rect.top  = 0;
      rect.bottom = disp->h;
      SetWindowPos(win_disp->window, 0, rect.left, rect.top,
             rect.right - rect.left, rect.bottom - rect.top,
             SWP_NOZORDER | SWP_FRAMECHANGED);
   }

   /* Yep, the following is really needed sometimes. */
   /* <rohannessian> Win98/2k/XP's window forground rules don't let us
    * make our window the topmost window on launch. This causes issues on 
    * full-screen apps, as DInput loses input focus on them.
    * We use this trick to force the window to be topmost, when switching
    * to full-screen only. Note that this only works for Win98 and greater.
    * Win95 will ignore our SystemParametersInfo() calls.
    * 
    * See http://support.microsoft.com:80/support/kb/articles/Q97/9/25.asp
    * for details.
    */
   {
      WORD lock_time;
      HWND wnd = win_disp->window;

#define SPI_GETFOREGROUNDLOCKTIMEOUT 0x2000
#define SPI_SETFOREGROUNDLOCKTIMEOUT 0x2001
      if (disp->flags & ALLEGRO_FULLSCREEN) {
         SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT,
               0, (LPVOID)&lock_time, 0);
         SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT,
               0, (LPVOID)0, SPIF_SENDWININICHANGE | SPIF_UPDATEINIFILE);
      }

      ShowWindow(wnd, SW_SHOWNORMAL);
      SetForegroundWindow(wnd);
      /* In some rare cases, it doesn't seem to work without the loop. And we
       * absolutely need this to succeed, else we trap the user in a
       * fullscreen window without input.
       */
      while (GetForegroundWindow() != wnd) {
         al_rest(0.01);
         SetForegroundWindow(wnd);
      }
      UpdateWindow(wnd);

      if (disp->flags & ALLEGRO_FULLSCREEN) {
         SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT,
              0, (LPVOID)(DWORD)lock_time, SPIF_SENDWININICHANGE | SPIF_UPDATEINIFILE);
      }
#undef SPI_GETFOREGROUNDLOCKTIMEOUT
#undef SPI_SETFOREGROUNDLOCKTIMEOUT
   }

   /* get the device context of our window */
   wgl_disp->dc = GetDC(win_disp->window);

   win_disp->thread_ended = false;
   win_disp->end_thread = false;
   ndp->init_failed = false;
   SetEvent(ndp->AckEvent);

   while (!win_disp->end_thread) {
      if (WaitMessage()) {
         /* messages are waiting in the queue */
         while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
            if (GetMessage(&msg, NULL, 0, 0)) {
               DispatchMessage(&msg);
            }
            else {
               goto End;
            }
         }
      }
      else {
         ALLEGRO_ERROR("Wait failed.\n");
         break;
      }
   }

End:
   if (wgl_disp->glrc) {
      wglDeleteContext(wgl_disp->glrc);
      wgl_disp->glrc = NULL;
   }
   if (wgl_disp->dc) {
      ReleaseDC(win_disp->window, wgl_disp->dc);
      wgl_disp->dc = NULL;
   }

   if (disp->flags & ALLEGRO_FULLSCREEN && !_wgl_do_not_change_display_mode) {
      ChangeDisplaySettings(NULL, 0);
   }

   if (win_disp->window) {
      DestroyWindow(win_disp->window);
      win_disp->window = NULL;
   }

   ALLEGRO_INFO("wgl display thread exits\n");
   win_disp->thread_ended = true;
}


static void wgl_flip_display(ALLEGRO_DISPLAY *d)
{
   ALLEGRO_DISPLAY_WGL* disp = (ALLEGRO_DISPLAY_WGL*)d;
   if (d->extra_settings.settings[ALLEGRO_SINGLE_BUFFER])
      glFlush();
   else
      SwapBuffers(disp->dc);
}


static void wgl_update_display_region(ALLEGRO_DISPLAY *d,
                                      int x, int y, int width, int height)
{
   if (al_get_opengl_extension_list()->ALLEGRO_WGL_WIN_swap_hint) {
      /* FIXME: This is just a driver hint and there is no guarantee that the
       * contens of the front buffer outside the given rectangle will be preserved,
       * thus we should really return false here and do nothing. */
      ALLEGRO_DISPLAY_WGL* disp = (ALLEGRO_DISPLAY_WGL*)d;
      wglAddSwapHintRectWIN(x, y, width, height);
      glFlush();
      SwapBuffers(disp->dc);
      return;
   }
   wgl_flip_display(d);
}


static bool wgl_resize_display(ALLEGRO_DISPLAY *d, int width, int height)
{
   ALLEGRO_DISPLAY_WGL *wgl_disp = (ALLEGRO_DISPLAY_WGL *)d;
   ALLEGRO_DISPLAY *ogl_disp = (ALLEGRO_DISPLAY *)d;
   ALLEGRO_DISPLAY_WIN *win_disp = (ALLEGRO_DISPLAY_WIN *)d;

   if (d->flags & ALLEGRO_FULLSCREEN) {
      ALLEGRO_BITMAP *target_bmp;
      _AL_VECTOR disp_bmps;
      bool was_backbuffer = false;
      size_t i;

      target_bmp = al_get_target_bitmap();
      if (target_bmp->vt)
         was_backbuffer = ((ALLEGRO_BITMAP_OGL*)target_bmp)->is_backbuffer;

      /* Remeber display bitmaps. */
      _al_vector_init(&disp_bmps, sizeof(ALLEGRO_BITMAP*));
      for (i = 0; i < _al_vector_size(&d->bitmaps); i++) {
         ALLEGRO_BITMAP **dis = _al_vector_ref(&d->bitmaps, i);
         ALLEGRO_BITMAP **mem = _al_vector_alloc_back(&disp_bmps);
         *mem = *dis;
      }

      /* This flag prevents from switching to desktop resolution in between. */
      _wgl_do_not_change_display_mode = true;
      destroy_display_internals(wgl_disp);
      _wgl_do_not_change_display_mode = false;

      d->w = width;
      d->h = height;
      if (!create_display_internals(wgl_disp))
         return false;

      /* Reupload bitmaps. */
      while (_al_vector_is_nonempty(&disp_bmps)) {
         ALLEGRO_BITMAP **back = _al_vector_ref_back(&disp_bmps);
         _al_convert_to_display_bitmap(*back);
         _al_vector_delete_at(&disp_bmps, _al_vector_size(&disp_bmps) - 1);
      }

      /* We have a new backbuffer now. */
      if (was_backbuffer)
         al_set_target_bitmap(al_get_backbuffer());
   }
   else {
      RECT win_size;
      WINDOWINFO wi;

      win_size.left = 0;
      win_size.top = 0;
      win_size.right = width;
      win_size.bottom = height;

      wi.cbSize = sizeof(WINDOWINFO);
      GetWindowInfo(win_disp->window, &wi);

      AdjustWindowRectEx(&win_size, wi.dwStyle, false, wi.dwExStyle);

      if (!SetWindowPos(win_disp->window, HWND_TOP,
         0, 0,
         win_size.right - win_size.left,
         win_size.bottom - win_size.top,
         SWP_NOMOVE|SWP_NOZORDER))
            return false;

      d->w = width;
      d->h = height;

      _al_ogl_resize_backbuffer(ogl_disp->ogl_extras->backbuffer, width, height);

      setup_gl(d);
   }

   return true;
}


static bool wgl_acknowledge_resize(ALLEGRO_DISPLAY *d)
{
   WINDOWINFO wi;
   ALLEGRO_DISPLAY_WIN *win_disp = (ALLEGRO_DISPLAY_WIN *)d;
   ALLEGRO_DISPLAY *ogl_disp = (ALLEGRO_DISPLAY *)d;
   int w, h;

   wi.cbSize = sizeof(WINDOWINFO);
   GetWindowInfo(win_disp->window, &wi);
   w = wi.rcClient.right - wi.rcClient.left;
   h = wi.rcClient.bottom - wi.rcClient.top;

   d->w = w;
   d->h = h;

   _al_ogl_resize_backbuffer(ogl_disp->ogl_extras->backbuffer, w, h);

   setup_gl(d);

   return true;
}


static bool wgl_is_compatible_bitmap(ALLEGRO_DISPLAY *display, ALLEGRO_BITMAP *bitmap)
{
   /* Note: WGL driver doesn't support sharing of resources between contexts,
    * thus all bitmaps are tied to the display which was current at the time
    * al_create_bitmap was called.
    */
   return display == bitmap->display;
}


static void wgl_switch_in(ALLEGRO_DISPLAY *display)
{
   ALLEGRO_DISPLAY_WIN *win_disp = (ALLEGRO_DISPLAY_WIN *)display;

   if (al_is_mouse_installed())
      al_set_mouse_range(win_disp->mouse_range_x1, win_disp->mouse_range_y1,
         win_disp->mouse_range_x2, win_disp->mouse_range_y2);
}


static void wgl_switch_out(ALLEGRO_DISPLAY *display)
{
   (void)display;
}


static void wgl_set_window_position(ALLEGRO_DISPLAY *display, int x, int y)
{
   _al_win_set_window_position(((ALLEGRO_DISPLAY_WIN *)display)->window, x, y);
}


static void wgl_get_window_position(ALLEGRO_DISPLAY *display, int *x, int *y)
{
   _al_win_get_window_position(((ALLEGRO_DISPLAY_WIN *)display)->window, x, y);
}


static void wgl_toggle_frame(ALLEGRO_DISPLAY *display, bool onoff)
{
   _al_win_toggle_window_frame(
      display,
      ((ALLEGRO_DISPLAY_WIN *)display)->window,
      display->w, display->h, onoff);
}


/* Obtain a reference to this driver. */
ALLEGRO_DISPLAY_INTERFACE *_al_display_wgl_driver(void)
{
   if (vt) return vt;

   vt = _AL_MALLOC(sizeof *vt);
   memset(vt, 0, sizeof *vt);

   vt->create_display = wgl_create_display;
   vt->destroy_display = wgl_destroy_display;
   vt->resize_display = wgl_resize_display;
   vt->set_current_display = wgl_set_current_display;
   vt->flip_display = wgl_flip_display;
   vt->update_display_region = wgl_update_display_region;
   vt->acknowledge_resize = wgl_acknowledge_resize;
   vt->create_bitmap = _al_ogl_create_bitmap;
   vt->create_sub_bitmap = _al_ogl_create_sub_bitmap;
   vt->get_backbuffer = _al_ogl_get_backbuffer;
   vt->get_frontbuffer = _al_ogl_get_backbuffer;
   vt->set_target_bitmap = _al_ogl_set_target_bitmap;
   vt->is_compatible_bitmap = wgl_is_compatible_bitmap;
   vt->switch_out = wgl_switch_in;
   vt->switch_in = wgl_switch_out;

   vt->create_mouse_cursor = _al_win_create_mouse_cursor;
   vt->destroy_mouse_cursor = _al_win_destroy_mouse_cursor;
   vt->set_mouse_cursor = _al_win_set_mouse_cursor;
   vt->set_system_mouse_cursor = _al_win_set_system_mouse_cursor;
   vt->show_mouse_cursor = _al_win_show_mouse_cursor;
   vt->hide_mouse_cursor = _al_win_hide_mouse_cursor;

   vt->set_icon = _al_win_set_display_icon;
   vt->set_window_position = wgl_set_window_position;
   vt->get_window_position = wgl_get_window_position;
   vt->toggle_frame = wgl_toggle_frame;
   vt->set_window_title = _al_win_set_window_title;
   _al_ogl_add_drawing_functions(vt);

   return vt;
}

int _al_wgl_get_num_display_modes(int format, int refresh_rate, int flags)
{
   DEVMODE dm;   
   int count = 0;

   /* FIXME: Ignoring format.
    * To get a list of pixel formats we have to create a window with a valid DC, which
    * would even require to change the video mode in fullscreen cases and we really
    * don't want to do that.
    */
   (void)format;
   (void)refresh_rate;
   (void)flags;

   memset(&dm, 0, sizeof(dm));
   dm.dmSize = sizeof(dm);

   while (EnumDisplaySettings(NULL, count, &dm) != false) {
      count++;
   }

   return count;
}


ALLEGRO_DISPLAY_MODE *_al_wgl_get_display_mode(int index, int format,
                                               int refresh_rate, int flags,
                                               ALLEGRO_DISPLAY_MODE *mode)
{
   DEVMODE dm;   

   /*
    * FIXME: see the comment in _al_wgl_get_num_display_modes
    */
   (void)format;
   (void)refresh_rate;
   (void)flags;

   memset(&dm, 0, sizeof(dm));
   dm.dmSize = sizeof(dm);

   if (!EnumDisplaySettings(NULL, index, &dm))
      return NULL;

   mode->width = dm.dmPelsWidth;
   mode->height = dm.dmPanningHeight;
   mode->refresh_rate = dm.dmDisplayFrequency;

   return mode;
}


int _al_wgl_get_num_video_adapters(void)
{
   DISPLAY_DEVICE dd;
   int count = 0;
   int c = 0;

   memset(&dd, 0, sizeof(dd));
   dd.cb = sizeof(dd);

   while (EnumDisplayDevices(NULL, count, &dd, 0) != false) {
      if (dd.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)
         c++;
      count++;
   }

   return c;
}


void _al_wgl_get_monitor_info(int adapter, ALLEGRO_MONITOR_INFO *info)
{
   DISPLAY_DEVICE dd;
   DEVMODE dm;

   memset(&dd, 0, sizeof(dd));
   dd.cb = sizeof(dd);
   EnumDisplayDevices(NULL, adapter, &dd, 0);

   memset(&dm, 0, sizeof(dm));
   dm.dmSize = sizeof(dm);
   EnumDisplaySettings(dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm);

   ASSERT(dm.dmFields & DM_PELSHEIGHT);
   ASSERT(dm.dmFields & DM_PELSWIDTH);
   ASSERT(dm.dmFields & DM_POSITION);

   info->x1 = dm.dmPosition.x;
   info->y1 = dm.dmPosition.y;
   info->x2 = info->x1 + dm.dmPelsWidth;
   info->y2 = info->y1 + dm.dmPelsHeight;
}

static ALLEGRO_EXTRA_DISPLAY_SETTINGS **cached_eds = NULL;
static int cached_eds_size = 0;

int _al_wgl_get_num_display_formats()
{
   ALLEGRO_SYSTEM *system = (void *)al_system_driver();
   int eds_count = 0;
   bool force_old = false;

   if (cached_eds) {
      return cached_eds_size;
   }

   if (system->config) {
      const char *selection_mode;
      selection_mode = al_get_config_value(system->config, "graphics",
                          "config_selection");
      if (selection_mode && selection_mode[0] != '\0') {
         if (!stricmp(selection_mode, "old"))
            force_old = true;
         else if (!stricmp(selection_mode, "new"))
            force_old = false;
      }
   }

   if (!force_old)
      cached_eds = get_available_pixel_formats_ext(&eds_count);
   if (!cached_eds) {
      HWND testwnd;
      HDC testdc;

      testwnd = _al_win_create_hidden_window();
      if (!testwnd)
         return 0;
      testdc = GetDC(testwnd);

      cached_eds = get_available_pixel_formats_old(&eds_count, testdc);

      if (testwnd) {
         ReleaseDC(testwnd, testdc);
         DestroyWindow(testwnd);
      }
   }

   cached_eds_size = eds_count;
   return eds_count;
}


int _al_wgl_get_display_format_option(int i, int option)
{
   if (!cached_eds)
      _al_wgl_get_num_display_formats();

   if (!cached_eds)
      return -1;

   if (i >= cached_eds_size || option >= ALLEGRO_DISPLAY_OPTIONS_COUNT)
      return -1; /* may be a valid value */

   return cached_eds[i]->settings[option];
}


void _al_wgl_set_new_display_format(int i)
{
   int j;

   if (i >= cached_eds_size || !cached_eds)
      return;

   for (j = 0; j < ALLEGRO_DISPLAY_OPTIONS_COUNT; j++) {
      al_set_new_display_option(j, cached_eds[i]->settings[j], ALLEGRO_REQUIRE);
   }
}


/* vi: set sts=3 sw=3 et: */

