/*
 * ReactOS Generic Framebuffer display driver
 *
 * Copyright (C) 2004 Filip Navara
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include "framebuf.h"

/*
 * GetAvailableModes
 *
 * Calls the miniport to get the list of modes supported by the kernel driver,
 * and returns the list of modes supported by the display driver.
 */

DWORD
GetAvailableModes(
   HANDLE hDriver,
   PVIDEO_MODE_INFORMATION *ModeInfo,
   DWORD *ModeInfoSize)
{
   ULONG ulTemp;
   VIDEO_NUM_MODES Modes;
   PVIDEO_MODE_INFORMATION ModeInfoPtr;

   /*
    * Get the number of modes supported by the mini-port
    */

   if (EngDeviceIoControl(hDriver, IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES, NULL,
                          0, &Modes, sizeof(VIDEO_NUM_MODES), &ulTemp))
   {
      return 0;
   }

   *ModeInfoSize = Modes.ModeInformationLength;

   /*
    * Allocate the buffer for the miniport to write the modes in.
    */

   *ModeInfo = (PVIDEO_MODE_INFORMATION)EngAllocMem(0, Modes.NumModes *
      Modes.ModeInformationLength, ALLOC_TAG);

   if (*ModeInfo == NULL)
   {
      return 0;
   }

   /*
    * Ask the miniport to fill in the available modes.
    */

   if (EngDeviceIoControl(hDriver, IOCTL_VIDEO_QUERY_AVAIL_MODES, NULL, 0,
                          *ModeInfo, Modes.NumModes * Modes.ModeInformationLength,
                          &ulTemp))
   {
      EngFreeMem(*ModeInfo);
      *ModeInfo = (PVIDEO_MODE_INFORMATION)NULL;
      return 0;
   }

   /*
    * Now see which of these modes are supported by the display driver.
    * As an internal mechanism, set the length to 0 for the modes we
    * DO NOT support.
    */

   ulTemp = Modes.NumModes;
   ModeInfoPtr = *ModeInfo;

   /*
    * Mode is rejected if it is not one plane, or not graphics, or is not
    * one of 8, 16 or 32 bits per pel.
    */

   while (ulTemp--)
   {
      if ((ModeInfoPtr->NumberOfPlanes != 1) ||
          !(ModeInfoPtr->AttributeFlags & VIDEO_MODE_GRAPHICS) ||
          ((ModeInfoPtr->BitsPerPlane != 8) &&
           (ModeInfoPtr->BitsPerPlane != 16) &&
           (ModeInfoPtr->BitsPerPlane != 24) &&
           (ModeInfoPtr->BitsPerPlane != 32)))
      {
         ModeInfoPtr->Length = 0;
      }

      ModeInfoPtr = (PVIDEO_MODE_INFORMATION)
         (((PUCHAR)ModeInfoPtr) + Modes.ModeInformationLength);
   }

   return Modes.NumModes;
}

BOOL
IntInitScreenInfo(
   PPDEV ppdev,
   LPDEVMODEW pDevMode,
   PGDIINFO pGdiInfo,
   PDEVINFO pDevInfo)
{
   ULONG ModeCount;
   ULONG ModeInfoSize;
   PVIDEO_MODE_INFORMATION ModeInfo, ModeInfoPtr, SelectedMode = NULL;
   VIDEO_COLOR_CAPABILITIES ColorCapabilities;
   LOGFONTW SystemFont = {16, 7, 0, 0, 700, 0, 0, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, VARIABLE_PITCH | FF_DONTCARE, L"System"};
   LOGFONTW AnsiVariableFont = {12, 9, 0, 0, 400, 0, 0, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_STROKE_PRECIS, PROOF_QUALITY, VARIABLE_PITCH | FF_DONTCARE, L"MS Sans Serif"};
   LOGFONTW AnsiFixedFont = {12, 9, 0, 0, 400, 0, 0, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_STROKE_PRECIS, PROOF_QUALITY, FIXED_PITCH | FF_DONTCARE, L"Courier"};
   ULONG Temp;

   /*
    * Call miniport to get information about video modes.
    */

   ModeCount = GetAvailableModes(ppdev->hDriver, &ModeInfo, &ModeInfoSize);
   if (ModeCount == 0)
   {
      return FALSE;
   }

   /*
    * Select the video mode depending on the info passed in pDevMode.
    */

   if (pDevMode->dmPelsWidth == 0 && pDevMode->dmPelsHeight == 0 &&
       pDevMode->dmBitsPerPel == 0 && pDevMode->dmDisplayFrequency == 0)
   {
      ModeInfoPtr = ModeInfo;
      while (ModeCount-- > 0)
      {
         if (ModeInfoPtr->Length == 0)
         {
            ModeInfoPtr = (PVIDEO_MODE_INFORMATION)
               (((PUCHAR)ModeInfoPtr) + ModeInfoSize);
            continue;
         }
         SelectedMode = ModeInfoPtr;
         break;
      }
   }
   else
   {
      ModeInfoPtr = ModeInfo;
      while (ModeCount-- > 0)
      {
         if (ModeInfoPtr->Length > 0 &&
	     pDevMode->dmPelsWidth == ModeInfoPtr->VisScreenWidth &&
             pDevMode->dmPelsHeight == ModeInfoPtr->VisScreenHeight &&
             pDevMode->dmBitsPerPel == (ModeInfoPtr->BitsPerPlane *
                                        ModeInfoPtr->NumberOfPlanes) &&
             pDevMode->dmDisplayFrequency == ModeInfoPtr->Frequency)
         {
            SelectedMode = ModeInfoPtr;
            break;
         }

         ModeInfoPtr = (PVIDEO_MODE_INFORMATION)
            (((PUCHAR)ModeInfoPtr) + ModeInfoSize);
      }
   }

   if (SelectedMode == NULL)
   {
      EngFreeMem(ModeInfo);
      return FALSE;
   }

   /*
    * Fill in the GDIINFO data structure with the information returned from
    * the kernel driver.
    */

   ppdev->ModeIndex = SelectedMode->ModeIndex;
   ppdev->ScreenWidth = SelectedMode->VisScreenWidth;
   ppdev->ScreenHeight = SelectedMode->VisScreenHeight;
   ppdev->ScreenDelta = SelectedMode->ScreenStride;
   ppdev->BitsPerPixel = SelectedMode->BitsPerPlane * SelectedMode->NumberOfPlanes;

   ppdev->MemWidth = SelectedMode->VideoMemoryBitmapWidth;
   ppdev->MemHeight = SelectedMode->VideoMemoryBitmapHeight;

   ppdev->RedMask = SelectedMode->RedMask;
   ppdev->GreenMask = SelectedMode->GreenMask;
   ppdev->BlueMask = SelectedMode->BlueMask;

   pGdiInfo->ulVersion = GDI_DRIVER_VERSION;
   pGdiInfo->ulTechnology = DT_RASDISPLAY;
   pGdiInfo->ulHorzSize = SelectedMode->XMillimeter;
   pGdiInfo->ulVertSize = SelectedMode->YMillimeter;
   pGdiInfo->ulHorzRes = SelectedMode->VisScreenWidth;
   pGdiInfo->ulVertRes = SelectedMode->VisScreenHeight;
   pGdiInfo->ulPanningHorzRes = SelectedMode->VisScreenWidth;
   pGdiInfo->ulPanningVertRes = SelectedMode->VisScreenHeight;
   pGdiInfo->cBitsPixel = SelectedMode->BitsPerPlane;
   pGdiInfo->cPlanes = SelectedMode->NumberOfPlanes;
   pGdiInfo->ulVRefresh = SelectedMode->Frequency;
   pGdiInfo->ulBltAlignment = 1;
   pGdiInfo->ulLogPixelsX = pDevMode->dmLogPixels;
   pGdiInfo->ulLogPixelsY = pDevMode->dmLogPixels;
   pGdiInfo->flTextCaps = TC_RA_ABLE;
   pGdiInfo->flRaster = 0;
   pGdiInfo->ulDACRed = SelectedMode->NumberRedBits;
   pGdiInfo->ulDACGreen = SelectedMode->NumberGreenBits;
   pGdiInfo->ulDACBlue = SelectedMode->NumberBlueBits;
   pGdiInfo->ulAspectX = 0x24;
   pGdiInfo->ulAspectY = 0x24;
   pGdiInfo->ulAspectXY = 0x33;
   pGdiInfo->xStyleStep = 1;
   pGdiInfo->yStyleStep = 1;
   pGdiInfo->denStyleStep = 3;
   pGdiInfo->ptlPhysOffset.x = 0;
   pGdiInfo->ptlPhysOffset.y = 0;
   pGdiInfo->szlPhysSize.cx = 0;
   pGdiInfo->szlPhysSize.cy = 0;

   /*
    * Try to get the color info from the miniport.
    */

   if (!EngDeviceIoControl(ppdev->hDriver, IOCTL_VIDEO_QUERY_COLOR_CAPABILITIES,
                           NULL, 0, &ColorCapabilities,
                           sizeof(VIDEO_COLOR_CAPABILITIES), &Temp))
   {
      pGdiInfo->ciDevice.Red.x = ColorCapabilities.RedChromaticity_x;
      pGdiInfo->ciDevice.Red.y = ColorCapabilities.RedChromaticity_y;
      pGdiInfo->ciDevice.Green.x = ColorCapabilities.GreenChromaticity_x;
      pGdiInfo->ciDevice.Green.y = ColorCapabilities.GreenChromaticity_y;
      pGdiInfo->ciDevice.Blue.x = ColorCapabilities.BlueChromaticity_x;
      pGdiInfo->ciDevice.Blue.y = ColorCapabilities.BlueChromaticity_y;
      pGdiInfo->ciDevice.AlignmentWhite.x = ColorCapabilities.WhiteChromaticity_x;
      pGdiInfo->ciDevice.AlignmentWhite.y = ColorCapabilities.WhiteChromaticity_y;
      pGdiInfo->ciDevice.AlignmentWhite.Y = ColorCapabilities.WhiteChromaticity_Y;
      if (ColorCapabilities.AttributeFlags & VIDEO_DEVICE_COLOR)
      {
         pGdiInfo->ciDevice.RedGamma = ColorCapabilities.RedGamma;
         pGdiInfo->ciDevice.GreenGamma = ColorCapabilities.GreenGamma;
         pGdiInfo->ciDevice.BlueGamma = ColorCapabilities.BlueGamma;
      }
      else
      {
         pGdiInfo->ciDevice.RedGamma = ColorCapabilities.WhiteGamma;
         pGdiInfo->ciDevice.GreenGamma = ColorCapabilities.WhiteGamma;
         pGdiInfo->ciDevice.BlueGamma = ColorCapabilities.WhiteGamma;
      }
   }
   else
   {
      pGdiInfo->ciDevice.Red.x = 6700;
      pGdiInfo->ciDevice.Red.y = 3300;
      pGdiInfo->ciDevice.Green.x = 2100;
      pGdiInfo->ciDevice.Green.y = 7100;
      pGdiInfo->ciDevice.Blue.x = 1400;
      pGdiInfo->ciDevice.Blue.y = 800;
      pGdiInfo->ciDevice.AlignmentWhite.x = 3127;
      pGdiInfo->ciDevice.AlignmentWhite.y = 3290;
      pGdiInfo->ciDevice.AlignmentWhite.Y = 0;
      pGdiInfo->ciDevice.RedGamma = 20000;
      pGdiInfo->ciDevice.GreenGamma = 20000;
      pGdiInfo->ciDevice.BlueGamma = 20000;
   }

   pGdiInfo->ciDevice.Red.Y = 0;
   pGdiInfo->ciDevice.Green.Y = 0;
   pGdiInfo->ciDevice.Blue.Y = 0;
   pGdiInfo->ciDevice.Cyan.x = 0;
   pGdiInfo->ciDevice.Cyan.y = 0;
   pGdiInfo->ciDevice.Cyan.Y = 0;
   pGdiInfo->ciDevice.Magenta.x = 0;
   pGdiInfo->ciDevice.Magenta.y = 0;
   pGdiInfo->ciDevice.Magenta.Y = 0;
   pGdiInfo->ciDevice.Yellow.x = 0;
   pGdiInfo->ciDevice.Yellow.y = 0;
   pGdiInfo->ciDevice.Yellow.Y = 0;
   pGdiInfo->ciDevice.MagentaInCyanDye = 0;
   pGdiInfo->ciDevice.YellowInCyanDye = 0;
   pGdiInfo->ciDevice.CyanInMagentaDye = 0;
   pGdiInfo->ciDevice.YellowInMagentaDye = 0;
   pGdiInfo->ciDevice.CyanInYellowDye = 0;
   pGdiInfo->ciDevice.MagentaInYellowDye = 0;
   pGdiInfo->ulDevicePelsDPI = 0;
   pGdiInfo->ulPrimaryOrder = PRIMARY_ORDER_CBA;
   pGdiInfo->ulHTPatternSize = HT_PATSIZE_4x4_M;
   pGdiInfo->flHTFlags = HT_FLAG_ADDITIVE_PRIMS;

   pDevInfo->flGraphicsCaps = 0;
   pDevInfo->lfDefaultFont = SystemFont;
   pDevInfo->lfAnsiVarFont = AnsiVariableFont;
   pDevInfo->lfAnsiFixFont = AnsiFixedFont;
   pDevInfo->cFonts = 0;
   pDevInfo->cxDither = 0;
   pDevInfo->cyDither = 0;
   pDevInfo->hpalDefault = 0;
   pDevInfo->flGraphicsCaps2 = 0;

   if (ppdev->BitsPerPixel == 8)
   {
      pGdiInfo->ulNumColors = 20;
      pGdiInfo->ulNumPalReg = 1 << ppdev->BitsPerPixel;
      pGdiInfo->ulHTOutputFormat = HT_FORMAT_8BPP;
      pDevInfo->flGraphicsCaps |= GCAPS_PALMANAGED;
      pDevInfo->iDitherFormat = BMF_8BPP;
      /* Assuming palette is orthogonal - all colors are same size. */
      ppdev->PaletteShift = 8 - pGdiInfo->ulDACRed;
   }
   else
   {
      pGdiInfo->ulNumColors = (ULONG)(-1);
      pGdiInfo->ulNumPalReg = 0;
      switch (ppdev->BitsPerPixel)
      {
         case 16:
            pGdiInfo->ulHTOutputFormat = HT_FORMAT_16BPP;
            pDevInfo->iDitherFormat = BMF_16BPP;
            break;

         case 24:
            pGdiInfo->ulHTOutputFormat = HT_FORMAT_24BPP;
            pDevInfo->iDitherFormat = BMF_24BPP;
            break;

         default:
            pGdiInfo->ulHTOutputFormat = HT_FORMAT_32BPP;
            pDevInfo->iDitherFormat = BMF_32BPP;
      }
   }

   EngFreeMem(ModeInfo);
   return TRUE;
}

/*
 * DrvGetModes
 *
 * Returns the list of available modes for the device.
 *
 * Status
 *    @implemented
 */

ULONG APIENTRY
DrvGetModes(
   IN HANDLE hDriver,
   IN ULONG cjSize,
   OUT DEVMODEW *pdm)
{
   ULONG ModeCount;
   ULONG ModeInfoSize;
   PVIDEO_MODE_INFORMATION ModeInfo, ModeInfoPtr;
   ULONG OutputSize;

   ModeCount = GetAvailableModes(hDriver, &ModeInfo, &ModeInfoSize);
   if (ModeCount == 0)
   {
      return 0;
   }

   if (pdm == NULL)
   {
      EngFreeMem(ModeInfo);
      return ModeCount * sizeof(DEVMODEW);
   }

   /*
    * Copy the information about supported modes into the output buffer.
    */

   OutputSize = 0;
   ModeInfoPtr = ModeInfo;

   while (ModeCount-- > 0)
   {
      if (ModeInfoPtr->Length == 0)
      {
         ModeInfoPtr = (PVIDEO_MODE_INFORMATION)(((ULONG_PTR)ModeInfoPtr) + ModeInfoSize);
         continue;
      }

      memset(pdm, 0, sizeof(DEVMODEW));
      memcpy(pdm->dmDeviceName, DEVICE_NAME, sizeof(DEVICE_NAME));
      pdm->dmSpecVersion =
      pdm->dmDriverVersion = DM_SPECVERSION;
      pdm->dmSize = sizeof(DEVMODEW);
      pdm->dmDriverExtra = 0;
      pdm->dmBitsPerPel = ModeInfoPtr->NumberOfPlanes * ModeInfoPtr->BitsPerPlane;
      pdm->dmPelsWidth = ModeInfoPtr->VisScreenWidth;
      pdm->dmPelsHeight = ModeInfoPtr->VisScreenHeight;
      pdm->dmDisplayFrequency = ModeInfoPtr->Frequency;
      pdm->dmDisplayFlags = 0;
      pdm->dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT |
                      DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;

      ModeInfoPtr = (PVIDEO_MODE_INFORMATION)(((ULONG_PTR)ModeInfoPtr) + ModeInfoSize);
      pdm = (LPDEVMODEW)(((ULONG_PTR)pdm) + sizeof(DEVMODEW));
      OutputSize += sizeof(DEVMODEW);
   }

   EngFreeMem(ModeInfo);
   return OutputSize;
}
