/*
 *  ReactOS W32 Subsystem
 *  Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
 *
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/* $Id: driver.c 22124 2006-05-31 12:28:30Z hpoussin $
 *
 * GDI Driver support routines
 * (mostly swiped from Wine)
 *
 */

#include <w32k.h>

#define NDEBUG
#include <debug.h>

/* #define TRACE_DRV_CALLS to get a log of all calls into the display driver. */
#undef TRACE_DRV_CALLS

typedef struct _GRAPHICS_DRIVER
{
  PWSTR  Name;
  PGD_ENABLEDRIVER  EnableDriver;
  int  ReferenceCount;
  struct _GRAPHICS_DRIVER  *Next;
} GRAPHICS_DRIVER, *PGRAPHICS_DRIVER;

static PGRAPHICS_DRIVER  DriverList;
static PGRAPHICS_DRIVER  GenericDriver = 0;

BOOL DRIVER_RegisterDriver(LPCWSTR  Name, PGD_ENABLEDRIVER  EnableDriver)
{
  PGRAPHICS_DRIVER  Driver = ExAllocatePoolWithTag(PagedPool, sizeof(*Driver), TAG_DRIVER);
  DPRINT( "DRIVER_RegisterDriver( Name: %S )\n", Name );
  if (!Driver)  return  FALSE;
  Driver->ReferenceCount = 0;
  Driver->EnableDriver = EnableDriver;
  if (Name)
  {
    Driver->Name = ExAllocatePoolWithTag(PagedPool,
                                         (wcslen(Name) + 1) * sizeof(WCHAR),
                                         TAG_DRIVER);
    if (Driver->Name == NULL)                                         
    {
        DPRINT1("Out of memory\n");
        ExFreePool(Driver);
        return  FALSE;
    }
    
    wcscpy(Driver->Name, Name);
    Driver->Next  = DriverList;
    DriverList = Driver;
    return  TRUE;
  }

  if (GenericDriver != NULL)
  {
    ExFreePool(Driver);
    return  FALSE;
  }

  GenericDriver = Driver;
  return  TRUE;
}

PGD_ENABLEDRIVER DRIVER_FindDDIDriver(LPCWSTR Name)
{
  static WCHAR DefaultPath[] = L"\\SystemRoot\\System32\\";
  static WCHAR DefaultExtension[] = L".DLL";
  SYSTEM_GDI_DRIVER_INFORMATION GdiDriverInfo;
  GRAPHICS_DRIVER *Driver = DriverList;
  NTSTATUS Status;
  LPWSTR FullName;
  LPCWSTR p;
  BOOL PathSeparatorFound;
  BOOL DotFound;
  UINT Size;

  DotFound = FALSE;
  PathSeparatorFound = FALSE;
  p = Name;
  while (L'\0' != *p)
  {
    if (L'\\' == *p || L'/' == *p)
    {
      PathSeparatorFound = TRUE;
      DotFound = FALSE;
    }
    else if (L'.' == *p)
    {
      DotFound = TRUE;
    }
    p++;
  }

  Size = (wcslen(Name) + 1) * sizeof(WCHAR);
  if (! PathSeparatorFound)
  {
    Size += sizeof(DefaultPath) - sizeof(WCHAR);
  }
  if (! DotFound)
  {
    Size += sizeof(DefaultExtension) - sizeof(WCHAR);
  }
  FullName = ExAllocatePoolWithTag(PagedPool, Size, TAG_DRIVER);
  if (NULL == FullName)
  {
    DPRINT1("Out of memory\n");
    return NULL;
  }
  if (PathSeparatorFound)
  {
    FullName[0] = L'\0';
  }
  else
  {
    wcscpy(FullName, DefaultPath);
  }
  wcscat(FullName, Name);
  if (! DotFound)
  {
    wcscat(FullName, DefaultExtension);
  }

  /* First see if the driver hasn't already been loaded */
  while (Driver && FullName)
  {
    if (!_wcsicmp( Driver->Name, FullName))
    {
      return Driver->EnableDriver;
    }
    Driver = Driver->Next;
  }

  /* If not, then load it */
  RtlInitUnicodeString (&GdiDriverInfo.DriverName, FullName);
  Status = ZwSetSystemInformation (SystemLoadGdiDriverInformation, &GdiDriverInfo, sizeof(SYSTEM_GDI_DRIVER_INFORMATION));
  ExFreePool(FullName);
  if (!NT_SUCCESS(Status)) return NULL;

  DRIVER_RegisterDriver( L"DISPLAY", GdiDriverInfo.EntryPoint);
  return (PGD_ENABLEDRIVER)GdiDriverInfo.EntryPoint;
}

#define BEGIN_FUNCTION_MAP() \
  ULONG i; \
  for (i = 0; i < DED->c; i++) \
  { \
    switch(DED->pdrvfn[i].iFunc) \
    {

#define END_FUNCTION_MAP() \
      default: \
        DPRINT1("Unsupported DDI function 0x%x\n", DED->pdrvfn[i].iFunc); \
        break; \
    } \
  }

#ifdef TRACE_DRV_CALLS

typedef struct _TRACEDRVINFO
  {
  unsigned Index;
  char *Name;
  PVOID DrvRoutine;
  }
TRACEDRVINFO, *PTRACEDRVINFO;

__asm__(
" .text\n"
"TraceDrv:\n"
" pushl %eax\n"
" call  _FindTraceInfo\n"
" add   $4,%esp\n"
" pushl %eax\n"
" pushl 4(%eax)\n"
" call  _DbgPrint\n"
" addl  $4,%esp\n"
" popl  %eax\n"
" mov   8(%eax),%eax\n"
" jmp   *%eax\n"
);

#define TRACEDRV_ROUTINE(function) \
unsigned TraceDrvIndex##function = INDEX_Drv##function; \
__asm__ ( \
" .text\n" \
"_Trace" #function ":\n" \
" movl _TraceDrvIndex" #function ",%eax\n" \
" jmp TraceDrv\n" \
); \
extern PVOID Trace##function;

TRACEDRV_ROUTINE(EnablePDEV)
TRACEDRV_ROUTINE(CompletePDEV)
TRACEDRV_ROUTINE(DisablePDEV)
TRACEDRV_ROUTINE(EnableSurface)
TRACEDRV_ROUTINE(DisableSurface)
TRACEDRV_ROUTINE(AssertMode)
TRACEDRV_ROUTINE(Offset)
TRACEDRV_ROUTINE(ResetPDEV)
TRACEDRV_ROUTINE(DisableDriver)
TRACEDRV_ROUTINE(CreateDeviceBitmap)
TRACEDRV_ROUTINE(DeleteDeviceBitmap)
TRACEDRV_ROUTINE(RealizeBrush)
TRACEDRV_ROUTINE(DitherColor)
TRACEDRV_ROUTINE(StrokePath)
TRACEDRV_ROUTINE(FillPath)
TRACEDRV_ROUTINE(StrokeAndFillPath)
TRACEDRV_ROUTINE(Paint)
TRACEDRV_ROUTINE(BitBlt)
TRACEDRV_ROUTINE(TransparentBlt)
TRACEDRV_ROUTINE(CopyBits)
TRACEDRV_ROUTINE(StretchBlt)
TRACEDRV_ROUTINE(StretchBltROP)
TRACEDRV_ROUTINE(SetPalette)
TRACEDRV_ROUTINE(TextOut)
TRACEDRV_ROUTINE(Escape)
TRACEDRV_ROUTINE(DrawEscape)
TRACEDRV_ROUTINE(QueryFont)
TRACEDRV_ROUTINE(QueryFontTree)
TRACEDRV_ROUTINE(QueryFontData)
TRACEDRV_ROUTINE(SetPointerShape)
TRACEDRV_ROUTINE(MovePointer)
TRACEDRV_ROUTINE(LineTo)
TRACEDRV_ROUTINE(SendPage)
TRACEDRV_ROUTINE(StartPage)
TRACEDRV_ROUTINE(EndDoc)
TRACEDRV_ROUTINE(StartDoc)
TRACEDRV_ROUTINE(GetGlyphMode)
TRACEDRV_ROUTINE(Synchronize)
TRACEDRV_ROUTINE(SaveScreenBits)
TRACEDRV_ROUTINE(GetModes)
TRACEDRV_ROUTINE(Free)
TRACEDRV_ROUTINE(DestroyFont)
TRACEDRV_ROUTINE(QueryFontCaps)
TRACEDRV_ROUTINE(LoadFontFile)
TRACEDRV_ROUTINE(UnloadFontFile)
TRACEDRV_ROUTINE(FontManagement)
TRACEDRV_ROUTINE(QueryTrueTypeTable)
TRACEDRV_ROUTINE(QueryTrueTypeOutline)
TRACEDRV_ROUTINE(GetTrueTypeFile)
TRACEDRV_ROUTINE(QueryFontFile)
TRACEDRV_ROUTINE(QueryAdvanceWidths)
TRACEDRV_ROUTINE(SetPixelFormat)
TRACEDRV_ROUTINE(DescribePixelFormat)
TRACEDRV_ROUTINE(SwapBuffers)
TRACEDRV_ROUTINE(StartBanding)
TRACEDRV_ROUTINE(NextBand)
TRACEDRV_ROUTINE(GetDirectDrawInfo)
TRACEDRV_ROUTINE(EnableDirectDraw)
TRACEDRV_ROUTINE(DisableDirectDraw)
TRACEDRV_ROUTINE(QuerySpoolType)
TRACEDRV_ROUTINE(GradientFill)
TRACEDRV_ROUTINE(SynchronizeSurface)
TRACEDRV_ROUTINE(AlphaBlend)

#define TRACEDRVINFO_ENTRY(function) \
    { INDEX_Drv##function, "Drv" #function "\n", NULL }
static TRACEDRVINFO TraceDrvInfo[] =
  {
    TRACEDRVINFO_ENTRY(EnablePDEV),
    TRACEDRVINFO_ENTRY(CompletePDEV),
    TRACEDRVINFO_ENTRY(DisablePDEV),
    TRACEDRVINFO_ENTRY(EnableSurface),
    TRACEDRVINFO_ENTRY(DisableSurface),
    TRACEDRVINFO_ENTRY(AssertMode),
    TRACEDRVINFO_ENTRY(Offset),
    TRACEDRVINFO_ENTRY(ResetPDEV),
    TRACEDRVINFO_ENTRY(DisableDriver),
    TRACEDRVINFO_ENTRY(CreateDeviceBitmap),
    TRACEDRVINFO_ENTRY(DeleteDeviceBitmap),
    TRACEDRVINFO_ENTRY(RealizeBrush),
    TRACEDRVINFO_ENTRY(DitherColor),
    TRACEDRVINFO_ENTRY(StrokePath),
    TRACEDRVINFO_ENTRY(FillPath),
    TRACEDRVINFO_ENTRY(StrokeAndFillPath),
    TRACEDRVINFO_ENTRY(Paint),
    TRACEDRVINFO_ENTRY(BitBlt),
    TRACEDRVINFO_ENTRY(TransparentBlt),
    TRACEDRVINFO_ENTRY(CopyBits),
    TRACEDRVINFO_ENTRY(StretchBlt),
    TRACEDRVINFO_ENTRY(StretchBltROP),
    TRACEDRVINFO_ENTRY(SetPalette),
    TRACEDRVINFO_ENTRY(TextOut),
    TRACEDRVINFO_ENTRY(Escape),
    TRACEDRVINFO_ENTRY(DrawEscape),
    TRACEDRVINFO_ENTRY(QueryFont),
    TRACEDRVINFO_ENTRY(QueryFontTree),
    TRACEDRVINFO_ENTRY(QueryFontData),
    TRACEDRVINFO_ENTRY(SetPointerShape),
    TRACEDRVINFO_ENTRY(MovePointer),
    TRACEDRVINFO_ENTRY(LineTo),
    TRACEDRVINFO_ENTRY(SendPage),
    TRACEDRVINFO_ENTRY(StartPage),
    TRACEDRVINFO_ENTRY(EndDoc),
    TRACEDRVINFO_ENTRY(StartDoc),
    TRACEDRVINFO_ENTRY(GetGlyphMode),
    TRACEDRVINFO_ENTRY(Synchronize),
    TRACEDRVINFO_ENTRY(SaveScreenBits),
    TRACEDRVINFO_ENTRY(GetModes),
    TRACEDRVINFO_ENTRY(Free),
    TRACEDRVINFO_ENTRY(DestroyFont),
    TRACEDRVINFO_ENTRY(QueryFontCaps),
    TRACEDRVINFO_ENTRY(LoadFontFile),
    TRACEDRVINFO_ENTRY(UnloadFontFile),
    TRACEDRVINFO_ENTRY(FontManagement),
    TRACEDRVINFO_ENTRY(QueryTrueTypeTable),
    TRACEDRVINFO_ENTRY(QueryTrueTypeOutline),
    TRACEDRVINFO_ENTRY(GetTrueTypeFile),
    TRACEDRVINFO_ENTRY(QueryFontFile),
    TRACEDRVINFO_ENTRY(QueryAdvanceWidths),
    TRACEDRVINFO_ENTRY(SetPixelFormat),
    TRACEDRVINFO_ENTRY(DescribePixelFormat),
    TRACEDRVINFO_ENTRY(SwapBuffers),
    TRACEDRVINFO_ENTRY(StartBanding),
    TRACEDRVINFO_ENTRY(NextBand),
    TRACEDRVINFO_ENTRY(GetDirectDrawInfo),
    TRACEDRVINFO_ENTRY(EnableDirectDraw),
    TRACEDRVINFO_ENTRY(DisableDirectDraw),
    TRACEDRVINFO_ENTRY(QuerySpoolType),
    TRACEDRVINFO_ENTRY(GradientFill),
    TRACEDRVINFO_ENTRY(SynchronizeSurface),
    TRACEDRVINFO_ENTRY(AlphaBlend)
  };

PTRACEDRVINFO
FindTraceInfo(unsigned Index)
{
  unsigned i;

  for (i = 0; i < sizeof(TraceDrvInfo) / sizeof(TRACEDRVINFO); i++)
    {
      if (TraceDrvInfo[i].Index == Index)
        {
          return TraceDrvInfo + i;
        }
    }

  return NULL;
}

#define DRIVER_FUNCTION(function) \
      case INDEX_Drv##function: \
        FindTraceInfo(INDEX_Drv##function)->DrvRoutine = DED->pdrvfn[i].pfn; \
        *(PVOID*)&DF->function = &Trace##function; \
        break
#else
#define DRIVER_FUNCTION(function) \
      case INDEX_Drv##function: \
        *(PVOID*)&DF->function = DED->pdrvfn[i].pfn; \
        break
#endif

BOOL DRIVER_BuildDDIFunctions(PDRVENABLEDATA  DED,
                               PDRIVER_FUNCTIONS  DF)
{
  BEGIN_FUNCTION_MAP();

    DRIVER_FUNCTION(EnablePDEV);
    DRIVER_FUNCTION(CompletePDEV);
    DRIVER_FUNCTION(DisablePDEV);
    DRIVER_FUNCTION(EnableSurface);
    DRIVER_FUNCTION(DisableSurface);
    DRIVER_FUNCTION(AssertMode);
    DRIVER_FUNCTION(Offset);
    DRIVER_FUNCTION(ResetPDEV);
    DRIVER_FUNCTION(DisableDriver);
    DRIVER_FUNCTION(CreateDeviceBitmap);
    DRIVER_FUNCTION(DeleteDeviceBitmap);
    DRIVER_FUNCTION(RealizeBrush);
    DRIVER_FUNCTION(DitherColor);
    DRIVER_FUNCTION(StrokePath);
    DRIVER_FUNCTION(FillPath);
    DRIVER_FUNCTION(StrokeAndFillPath);
    DRIVER_FUNCTION(Paint);
    DRIVER_FUNCTION(BitBlt);
    DRIVER_FUNCTION(TransparentBlt);
    DRIVER_FUNCTION(CopyBits);
    DRIVER_FUNCTION(StretchBlt);
    DRIVER_FUNCTION(StretchBltROP);
    DRIVER_FUNCTION(SetPalette);
    DRIVER_FUNCTION(TextOut);
    DRIVER_FUNCTION(Escape);
    DRIVER_FUNCTION(DrawEscape);
    DRIVER_FUNCTION(QueryFont);
    DRIVER_FUNCTION(QueryFontTree);
    DRIVER_FUNCTION(QueryFontData);
    DRIVER_FUNCTION(SetPointerShape);
    DRIVER_FUNCTION(MovePointer);
    DRIVER_FUNCTION(LineTo);
    DRIVER_FUNCTION(SendPage);
    DRIVER_FUNCTION(StartPage);
    DRIVER_FUNCTION(EndDoc);
    DRIVER_FUNCTION(StartDoc);
    DRIVER_FUNCTION(GetGlyphMode);
    DRIVER_FUNCTION(Synchronize);
    DRIVER_FUNCTION(SaveScreenBits);
    DRIVER_FUNCTION(GetModes);
    DRIVER_FUNCTION(Free);
    DRIVER_FUNCTION(DestroyFont);
    DRIVER_FUNCTION(QueryFontCaps);
    DRIVER_FUNCTION(LoadFontFile);
    DRIVER_FUNCTION(UnloadFontFile);
    DRIVER_FUNCTION(FontManagement);
    DRIVER_FUNCTION(QueryTrueTypeTable);
    DRIVER_FUNCTION(QueryTrueTypeOutline);
    DRIVER_FUNCTION(GetTrueTypeFile);
    DRIVER_FUNCTION(QueryFontFile);
    DRIVER_FUNCTION(QueryAdvanceWidths);
    DRIVER_FUNCTION(SetPixelFormat);
    DRIVER_FUNCTION(DescribePixelFormat);
    DRIVER_FUNCTION(SwapBuffers);
    DRIVER_FUNCTION(StartBanding);
    DRIVER_FUNCTION(NextBand);
    DRIVER_FUNCTION(GetDirectDrawInfo);
    DRIVER_FUNCTION(EnableDirectDraw);
    DRIVER_FUNCTION(DisableDirectDraw);
    DRIVER_FUNCTION(QuerySpoolType);
    DRIVER_FUNCTION(GradientFill);
    DRIVER_FUNCTION(SynchronizeSurface);
    DRIVER_FUNCTION(AlphaBlend);

  END_FUNCTION_MAP();

  return TRUE;
}

typedef LONG VP_STATUS;
typedef VP_STATUS (STDCALL *PMP_DRIVERENTRY)(PVOID, PVOID);

PFILE_OBJECT DRIVER_FindMPDriver(ULONG DisplayNumber)
{
  OBJECT_ATTRIBUTES ObjectAttributes;
  WCHAR DeviceNameBuffer[20];
  UNICODE_STRING DeviceName;
  IO_STATUS_BLOCK Iosb;
  HANDLE DisplayHandle;
  NTSTATUS Status;
  PFILE_OBJECT VideoFileObject;

  swprintf(DeviceNameBuffer, L"\\??\\DISPLAY%d", DisplayNumber + 1);
  RtlInitUnicodeString(&DeviceName, DeviceNameBuffer);
  InitializeObjectAttributes(&ObjectAttributes,
			     &DeviceName,
			     0,
			     NULL,
			     NULL);
  Status = ZwOpenFile(&DisplayHandle,
		      FILE_ALL_ACCESS,
		      &ObjectAttributes,
		      &Iosb,
		      0,
		      FILE_SYNCHRONOUS_IO_ALERT);
  if (NT_SUCCESS(Status))
    {
      Status = ObReferenceObjectByHandle(DisplayHandle,
                                         FILE_READ_DATA | FILE_WRITE_DATA,
                                         IoFileObjectType,
                                         KernelMode,
                                         (PVOID *)&VideoFileObject,
                                         NULL);
      ZwClose(DisplayHandle);
    }

  if (!NT_SUCCESS(Status))
    {
      DPRINT1("Unable to connect to miniport (Status %lx)\n", Status);
      DPRINT1("Perhaps the miniport wasn't loaded?\n");
      return(NULL);
    }

  return VideoFileObject;
}


BOOL DRIVER_UnregisterDriver(LPCWSTR  Name)
{
  PGRAPHICS_DRIVER  Driver = NULL;

  if (Name)
  {
    if (DriverList != NULL)
    {
      if (!_wcsicmp(DriverList->Name, Name))
      {
        Driver = DriverList;
        DriverList = DriverList->Next;
      }
      else
      {
        Driver = DriverList;
        while (Driver->Next && _wcsicmp(Driver->Name, Name))
        {
          Driver = Driver->Next;
        }
      }
    }
  }
  else
  {
    if (GenericDriver != NULL)
    {
      Driver = GenericDriver;
      GenericDriver = NULL;
    }
  }

  if (Driver != NULL)
  {
    ExFreePool(Driver->Name);
    ExFreePool(Driver);

    return  TRUE;
  }
  else
  {
    return  FALSE;
  }
}

INT DRIVER_ReferenceDriver (LPCWSTR  Name)
{
  GRAPHICS_DRIVER *Driver = DriverList;

  while (Driver && Name)
  {
    DPRINT( "Comparing %S to %S\n", Driver->Name, Name );
    if (!_wcsicmp( Driver->Name, Name))
    {
      return ++Driver->ReferenceCount;
    }
    Driver = Driver->Next;
  }
  DPRINT( "Driver %S not found to reference, generic count: %d\n", Name, GenericDriver->ReferenceCount );
  assert( GenericDriver != 0 );
  return ++GenericDriver->ReferenceCount;
}

INT DRIVER_UnreferenceDriver (LPCWSTR  Name)
{
  GRAPHICS_DRIVER *Driver = DriverList;

  while (Driver && Name)
  {
    DPRINT( "Comparing %S to %S\n", Driver->Name, Name );
    if (!_wcsicmp( Driver->Name, Name))
    {
      return --Driver->ReferenceCount;
    }
    Driver = Driver->Next;
  }
  DPRINT( "Driver '%S' not found to dereference, generic count: %d\n", Name, GenericDriver->ReferenceCount );
  assert( GenericDriver != 0 );
  return --GenericDriver->ReferenceCount;
}
/* EOF */
