You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
16670 lines
561 KiB
16670 lines
561 KiB
/******************************Module*Header*******************************\
|
|
* Module Name: drvsup.c *
|
|
* *
|
|
* Copyright (c) 1990-1999 Microsoft Corporation *
|
|
* *
|
|
* Display Driver management routines *
|
|
* *
|
|
* Andre Vachon -Andreva- *
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.hxx"
|
|
|
|
// The declaration of AlignRects has been removed from usergdi.h
|
|
// and has been explicitly added here. [dchinn]
|
|
extern "C" {
|
|
BOOL
|
|
AlignRects(
|
|
IN OUT LPRECT arc,
|
|
IN DWORD cCount,
|
|
IN DWORD iPrimary,
|
|
IN DWORD dwFlags);
|
|
}
|
|
|
|
extern
|
|
VOID APIENTRY GreSuspendDirectDrawEx(
|
|
HDEV hdev,
|
|
ULONG fl
|
|
);
|
|
|
|
extern
|
|
VOID APIENTRY GreResumeDirectDrawEx(
|
|
HDEV hdev,
|
|
ULONG fl
|
|
);
|
|
|
|
#pragma hdrstop
|
|
|
|
#include <wdmguid.h> // for GUID_DEVICE_INTERFACE_ARRIVAL/REMOVAL
|
|
|
|
#define INITGUID
|
|
#include <initguid.h>
|
|
#include "ntddvdeo.h"
|
|
|
|
#ifdef _HYDRA_
|
|
|
|
#include <regapi.h>
|
|
#include <winDDIts.h>
|
|
#include "muclean.hxx"
|
|
#include "winstaw.h"
|
|
|
|
extern PFILE_OBJECT G_RemoteVideoFileObject;
|
|
extern PFILE_OBJECT G_RemoteConnectionFileObject;
|
|
extern HANDLE G_RemoteConnectionChannel;
|
|
extern PBYTE G_PerformanceStatistics;
|
|
extern BOOL G_fConsole;
|
|
extern BOOL G_fDoubleDpi;
|
|
extern LPWSTR G_DisplayDriverNames;
|
|
|
|
#endif
|
|
|
|
#if TEXTURE_DEMO
|
|
|
|
/*
|
|
* Texture Demo
|
|
*/
|
|
|
|
ULONG gcTextures; // Count of textures
|
|
HDEV gahdevTexture[8]; // Array of texture PDEVs
|
|
BOOL gbTexture = FALSE; // TRUE if we're to do the texture demo
|
|
HDEV ghdevTextureParent; // Non-NULL if doing texture demo
|
|
LONG gcxTexture; // Texture size of TexEnablePDEV
|
|
LONG gcyTexture;
|
|
|
|
#define INDEX_DrvDemoTexture INDEX_DrvMovePanning
|
|
|
|
typedef struct _DEMOCOORDINATE
|
|
{
|
|
float fX;
|
|
float fY;
|
|
float fW;
|
|
float fU;
|
|
float fV;
|
|
float fZ;
|
|
} DEMOCOORDINATE;
|
|
|
|
typedef struct _DEMOQUAD
|
|
{
|
|
DEMOCOORDINATE V0;
|
|
DEMOCOORDINATE V1;
|
|
DEMOCOORDINATE V2;
|
|
DEMOCOORDINATE V3;
|
|
} DEMOQUAD;
|
|
|
|
BOOL APIENTRY DrvDemoTexture(
|
|
SURFOBJ *psoDst,
|
|
SURFOBJ *psoSrc,
|
|
CLIPOBJ *pco,
|
|
DEMOQUAD *pQuads,
|
|
ULONG cQuads
|
|
);
|
|
|
|
typedef BOOL (*PFN_DrvDemoTexture)(SURFOBJ*,SURFOBJ*,CLIPOBJ*,DEMOQUAD*,ULONG);
|
|
|
|
#endif // TEXTURE_DEMO
|
|
|
|
typedef enum _DISP_DRIVER_LOG {
|
|
MsgInvalidConfiguration = 1,
|
|
MsgInvalidDisplayDriver,
|
|
MsgInvalidOldDriver,
|
|
MsgInvalidDisplayMode,
|
|
MsgInvalidDisplay16Colors,
|
|
MsgInvalidUsingDefaultMode,
|
|
} DISP_DRIVER_LOG;
|
|
|
|
typedef enum _DISP_DRIVER_REGISTRY_TYPE {
|
|
DispDriverRegGlobal,
|
|
DispDriverRegHardwareProfile,
|
|
DispDriverRegHardwareProfileCreate,
|
|
DispDriverRegKey
|
|
} DISP_DRIVER_REGISTRY_TYPE;
|
|
|
|
typedef enum _GRAPHICS_STATE {
|
|
GraphicsStateFull = 1,
|
|
GraphicsStateNoAttach,
|
|
GraphicsStateAttachDisconnect
|
|
} GRAPHICS_STATE;
|
|
|
|
|
|
#define DM_INTERNAL_VALID_FLAGS \
|
|
(DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | \
|
|
DM_DISPLAYFLAGS | DM_LOGPIXELS | DM_PANNINGWIDTH | DM_PANNINGHEIGHT | \
|
|
DM_DISPLAYORIENTATION )
|
|
|
|
#define DDML_DRIVER -4
|
|
|
|
BOOL gbBaseVideo;
|
|
BOOL gbUpdateMonitor;
|
|
BOOL gbInvalidateDualView;
|
|
USHORT gdmLogPixels;
|
|
ULONG gcNextGlobalDeviceNumber;
|
|
ULONG gcNextGlobalPhysicalOutputNumber;
|
|
ULONG gcNextGlobalVirtualOutputNumber;
|
|
PGRAPHICS_DEVICE gpGraphicsDeviceList;
|
|
PGRAPHICS_DEVICE gpGraphicsDeviceListLast;
|
|
PGRAPHICS_DEVICE gPhysDispVGA; // VGA driver hack
|
|
GRAPHICS_DEVICE gFullscreenGraphicsDevice;
|
|
GRAPHICS_DEVICE gFeFullscreenGraphicsDevice;
|
|
// #if DBG
|
|
ULONG gcFailedModeChanges = 0;
|
|
// #endif
|
|
|
|
PPALETTE DrvRealizeHalftonePalette(HDEV hdevPalette, BOOL bForce);
|
|
|
|
|
|
|
|
BOOL
|
|
DrvSetDisconnectedGraphicsDevice(
|
|
BOOL bLocal);
|
|
|
|
|
|
VOID
|
|
DrvCleanupOneGraphicsDevice(PGRAPHICS_DEVICE pGraphicsDeviceList);
|
|
|
|
|
|
BOOL
|
|
DrvIsProtocolAlreadyKnown(VOID);
|
|
|
|
extern "C" USHORT gProtocolType;
|
|
|
|
ULONG gcRemoteNextGlobalDeviceNumber;
|
|
ULONG gcLocalNextGlobalDeviceNumber;
|
|
PGRAPHICS_DEVICE gpRemoteGraphicsDeviceList;
|
|
PGRAPHICS_DEVICE gpLocalGraphicsDeviceList;
|
|
PGRAPHICS_DEVICE gpRemoteGraphicsDeviceListLast;
|
|
PGRAPHICS_DEVICE gpLocalGraphicsDeviceListLast;
|
|
PGRAPHICS_DEVICE gpRemoteDiscGraphicsDevice;
|
|
PGRAPHICS_DEVICE gpLocalDiscGraphicsDevice;
|
|
ULONG gcLocalNextGlobalPhysicalOutputNumber = 1;
|
|
ULONG gcLocalNextGlobalVirtualOutputNumber = 1;
|
|
ULONG gcRemoteNextGlobalPhysicalOutputNumber = 1;
|
|
ULONG gcRemoteNextGlobalVirtualOutputNumber = 1;
|
|
|
|
|
|
#if MDEV_STACK_TRACE_LENGTH
|
|
LONG glMDEVTrace = 0;
|
|
MDEVRECORD gMDEVTrace[32];
|
|
const LONG gcMDEVTraceLength = sizeof(gMDEVTrace)/sizeof(gMDEVTrace[0]);
|
|
#endif
|
|
|
|
//
|
|
// Global driver list. This pointer points to the first driver in a
|
|
// singly linked list of drivers.
|
|
//
|
|
// We use this list to determine when we are called to load a driver to
|
|
// determine if the the driver image is already loaded.
|
|
// If the image is already loaded, we will just increment the reference count
|
|
// and then create a new PDEV.
|
|
//
|
|
|
|
PLDEV gpldevDrivers;
|
|
|
|
#if DBG
|
|
#define TRACE_SWITCH(str) { if (GreTraceDisplayDriverLoad) { KdPrint(str); } }
|
|
#else
|
|
#define TRACE_SWITCH(str)
|
|
#endif
|
|
|
|
PUCHAR gpFullscreenFrameBufPtr;
|
|
ULONG gpFullscreenFrameBufLength = 0;
|
|
|
|
LONG CModeChangeInProgress::lInModeChange = 0;
|
|
LONG CModeChangeInProgress::lNeedSyncFlush = 0;
|
|
LONG CModeChangeInProgress::lNeedTimerFlush = 0;
|
|
|
|
int
|
|
ConvertOutputToOem(
|
|
IN LPWSTR Source,
|
|
IN int SourceLength, // in chars
|
|
OUT LPSTR Target,
|
|
IN int TargetLength // in chars
|
|
)
|
|
/*
|
|
Converts SourceLength Unicode characters from Source into
|
|
not more than TargetLength Codepage characters at Target.
|
|
Returns the number characters put in Target. (0 if failure)
|
|
|
|
[ntcon\server\misc.c]
|
|
*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG Length;
|
|
|
|
// Can do this in place
|
|
Status = RtlUnicodeToOemN(Target,
|
|
TargetLength,
|
|
&Length,
|
|
Source,
|
|
SourceLength * sizeof(WCHAR)
|
|
);
|
|
if (NT_SUCCESS(Status)) {
|
|
return Length;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* TranslateOutputToOem
|
|
*
|
|
* routine to translate console PCHAR_INFO to the ASCII from Unicode
|
|
*
|
|
* [ntcon\server\fe\direct2.c]
|
|
\***************************************************************************/
|
|
|
|
NTSTATUS
|
|
TranslateOutputToOem(
|
|
OUT PCHAR_INFO OutputBuffer,
|
|
IN PCHAR_INFO InputBuffer,
|
|
IN DWORD Length
|
|
)
|
|
{
|
|
CHAR AsciiDbcs[2];
|
|
ULONG NumBytes;
|
|
|
|
while (Length--)
|
|
{
|
|
if (InputBuffer->Attributes & COMMON_LVB_LEADING_BYTE)
|
|
{
|
|
if (Length >= 2) // Safe DBCS in buffer ?
|
|
{
|
|
Length--;
|
|
NumBytes = sizeof(AsciiDbcs);
|
|
NumBytes = ConvertOutputToOem(&InputBuffer->Char.UnicodeChar,
|
|
1,
|
|
&AsciiDbcs[0],
|
|
NumBytes);
|
|
OutputBuffer->Char.AsciiChar = AsciiDbcs[0];
|
|
OutputBuffer->Attributes = InputBuffer->Attributes;
|
|
OutputBuffer++;
|
|
InputBuffer++;
|
|
OutputBuffer->Char.AsciiChar = AsciiDbcs[1];
|
|
OutputBuffer->Attributes = InputBuffer->Attributes;
|
|
OutputBuffer++;
|
|
InputBuffer++;
|
|
}
|
|
else
|
|
{
|
|
OutputBuffer->Char.AsciiChar = ' ';
|
|
OutputBuffer->Attributes = InputBuffer->Attributes & ~COMMON_LVB_SBCSDBCS;
|
|
OutputBuffer++;
|
|
InputBuffer++;
|
|
}
|
|
}
|
|
else if (! (InputBuffer->Attributes & COMMON_LVB_SBCSDBCS))
|
|
{
|
|
ConvertOutputToOem(&InputBuffer->Char.UnicodeChar,
|
|
1,
|
|
&OutputBuffer->Char.AsciiChar,
|
|
1);
|
|
OutputBuffer->Attributes = InputBuffer->Attributes;
|
|
OutputBuffer++;
|
|
InputBuffer++;
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* NtGdiFullscreenControl
|
|
*
|
|
* routine to support console calls to the video driver
|
|
*
|
|
* 01-Sep-1995 andreva Created
|
|
\***************************************************************************/
|
|
|
|
NTSTATUS
|
|
NtGdiFullscreenControl(
|
|
IN FULLSCREENCONTROL FullscreenCommand,
|
|
PVOID FullscreenInput,
|
|
DWORD FullscreenInputLength,
|
|
PVOID FullscreenOutput,
|
|
PULONG FullscreenOutputLength)
|
|
{
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
ULONG BytesReturned;
|
|
PVOID pCapBuffer = NULL;
|
|
ULONG cCapBuffer = 0;
|
|
ULONG ioctl = 0;
|
|
|
|
#define VERIFY_RANGE(base, length, max_length) \
|
|
(((ULONG_PTR)base < (ULONG_PTR)max_length) && \
|
|
((ULONG_PTR)length < (ULONG_PTR)max_length) && \
|
|
(((ULONG_PTR)base + (ULONG_PTR)length) < (ULONG_PTR)max_length))
|
|
|
|
//
|
|
// If this is not CSR, then fail the API call.
|
|
//
|
|
|
|
if (PsGetCurrentProcess() != gpepCSRSS)
|
|
{
|
|
return STATUS_PRIVILEGE_NOT_HELD;
|
|
}
|
|
|
|
//
|
|
// First validate the ioctl
|
|
//
|
|
|
|
switch(FullscreenCommand) {
|
|
|
|
case FullscreenControlEnable:
|
|
ioctl = IOCTL_VIDEO_ENABLE_VDM;
|
|
TRACE_SWITCH(("Switching: FullscreenControlEnable\n"));
|
|
break;
|
|
|
|
case FullscreenControlDisable:
|
|
ioctl = IOCTL_VIDEO_DISABLE_VDM;
|
|
TRACE_SWITCH(("Switching: FullscreenControlDisable\n"));
|
|
break;
|
|
|
|
case FullscreenControlSetCursorPosition:
|
|
if (gFeFullscreenGraphicsDevice.pDeviceHandle != NULL)
|
|
{
|
|
ioctl = IOCTL_FSVIDEO_SET_CURSOR_POSITION;
|
|
}
|
|
else
|
|
{
|
|
ioctl = IOCTL_VIDEO_SET_CURSOR_POSITION;
|
|
}
|
|
TRACE_SWITCH(("Switching: FullscreenControlSetCursorPosition\n"));
|
|
break;
|
|
|
|
case FullscreenControlSetCursorAttributes:
|
|
ioctl = IOCTL_VIDEO_SET_CURSOR_ATTR;
|
|
TRACE_SWITCH(("Switching: FullscreenControlSetCursorAttributes\n"));
|
|
break;
|
|
|
|
case FullscreenControlRegisterVdm:
|
|
ioctl = IOCTL_VIDEO_REGISTER_VDM;
|
|
TRACE_SWITCH(("Switching: FullscreenControlRegisterVdm\n"));
|
|
break;
|
|
|
|
case FullscreenControlSetPalette:
|
|
ioctl = IOCTL_VIDEO_SET_PALETTE_REGISTERS;
|
|
TRACE_SWITCH(("Switching: FullscreenControlSetPalette\n"));
|
|
break;
|
|
|
|
case FullscreenControlSetColors:
|
|
ioctl = IOCTL_VIDEO_SET_COLOR_REGISTERS;
|
|
TRACE_SWITCH(("Switching: FullscreenControlSetColors\n"));
|
|
break;
|
|
|
|
case FullscreenControlLoadFont:
|
|
ioctl = IOCTL_VIDEO_LOAD_AND_SET_FONT;
|
|
TRACE_SWITCH(("Switching: FullscreenControlLoadFont\n"));
|
|
break;
|
|
|
|
case FullscreenControlRestoreHardwareState:
|
|
ioctl = IOCTL_VIDEO_RESTORE_HARDWARE_STATE;
|
|
TRACE_SWITCH(("Switching: FullscreenControlRestoreHardwareState\n"));
|
|
break;
|
|
|
|
case FullscreenControlSaveHardwareState:
|
|
ioctl = IOCTL_VIDEO_SAVE_HARDWARE_STATE;
|
|
TRACE_SWITCH(("Switching: FullscreenControlSaveHardwareState\n"));
|
|
break;
|
|
|
|
|
|
case FullscreenControlCopyFrameBuffer:
|
|
case FullscreenControlReadFromFrameBuffer:
|
|
case FullscreenControlWriteToFrameBuffer:
|
|
case FullscreenControlReverseMousePointer:
|
|
case FullscreenControlCopyFrameBufferDB:
|
|
case FullscreenControlWriteToFrameBufferDB:
|
|
case FullscreenControlReverseMousePointerDB:
|
|
|
|
// TRACE_SWITCH(("Switching: Fullscreen output command\n"));
|
|
|
|
if (gFeFullscreenGraphicsDevice.pDeviceHandle != NULL)
|
|
{
|
|
/*
|
|
* Console Full Screen Video driver is available.
|
|
*/
|
|
switch(FullscreenCommand)
|
|
{
|
|
case FullscreenControlCopyFrameBufferDB:
|
|
ioctl = IOCTL_FSVIDEO_COPY_FRAME_BUFFER;
|
|
break;
|
|
case FullscreenControlWriteToFrameBufferDB:
|
|
ioctl = IOCTL_FSVIDEO_WRITE_TO_FRAME_BUFFER;
|
|
break;
|
|
case FullscreenControlReverseMousePointerDB:
|
|
ioctl = IOCTL_FSVIDEO_REVERSE_MOUSE_POINTER;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FullscreenControlSetMode:
|
|
TRACE_SWITCH(("Switching: Fullscreen setmode command\n"));
|
|
break;
|
|
|
|
|
|
case FullscreenControlSetScreenInformation:
|
|
if (gFeFullscreenGraphicsDevice.pDeviceHandle != NULL)
|
|
{
|
|
ioctl = IOCTL_FSVIDEO_SET_SCREEN_INFORMATION;
|
|
}
|
|
else
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
break;
|
|
|
|
case FullscreenControlSpecificVideoControl: // for specific NEC PC-98
|
|
__try
|
|
{
|
|
ProbeForRead(FullscreenInput, sizeof(DWORD), sizeof(DWORD));
|
|
RtlCopyMemory(&ioctl, FullscreenInput, sizeof(DWORD));
|
|
|
|
FullscreenInput = (PVOID)((PBYTE)FullscreenInput + sizeof(DWORD));
|
|
FullscreenInputLength -= sizeof(DWORD);
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
RIP("FullscreenControlSpecificVideoControl - error processing input buffer\n");
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
break;
|
|
|
|
|
|
default:
|
|
RIP("NtUserFullscreenControl: invalid IOCTL\n");
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
//
|
|
// If this is a frame buffer function, that we can just deal with the
|
|
// device directly, and not send any IOCTL to the device.
|
|
//
|
|
|
|
if (ioctl == 0)
|
|
{
|
|
CHAR Attribute;
|
|
|
|
//
|
|
// First get the frame buffer pointer for the device.
|
|
//
|
|
|
|
PUCHAR pFrameBuf = gpFullscreenFrameBufPtr;
|
|
ULONG_PTR FrameBufLen = (ULONG_PTR)gpFullscreenFrameBufLength;
|
|
PCHAR_INFO pCharInfo;
|
|
PCHAR_IMAGE_INFO pCharImageInfo;
|
|
LPDEVMODEW pDevmode = gFullscreenGraphicsDevice.devmodeInfo;
|
|
VIDEO_MODE VideoMode;
|
|
BOOLEAN modeFound = FALSE;
|
|
ULONG BytesReturned;
|
|
ULONG i;
|
|
DEVMODEW capturedDevMode;
|
|
|
|
|
|
//
|
|
// Assume success for all these operations.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
switch(FullscreenCommand) {
|
|
|
|
case FullscreenControlSetMode:
|
|
|
|
//
|
|
// Fullscreen VGA modes require us to call the miniport driver
|
|
// directly.
|
|
//
|
|
// Lets check the VGA Device handle, which is in the first entry
|
|
//
|
|
|
|
__try
|
|
{
|
|
ProbeForRead(FullscreenInput, sizeof(DEVMODEW), sizeof(USHORT));
|
|
RtlCopyMemory(&capturedDevMode, FullscreenInput, sizeof(DEVMODEW));
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
RIP("Fullscreen control - error processing input/output buffer\n");
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
if ((Status != STATUS_SUCCESS) ||
|
|
(gFullscreenGraphicsDevice.pDeviceHandle == NULL))
|
|
{
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// NOTE We know that if there is a vgacompatible device, then
|
|
// there are some text modes for it.
|
|
//
|
|
// NOTE !!!
|
|
// As a hack, lets use the mode number we stored in the DEVMODE
|
|
// a field we don't use
|
|
//
|
|
|
|
for (i = 0;
|
|
i < gFullscreenGraphicsDevice.cbdevmodeInfo;
|
|
i += sizeof(DEVMODEW), pDevmode += 1) {
|
|
|
|
//
|
|
// Check if this is the resolustion we are looking for.
|
|
//
|
|
|
|
if ((pDevmode->dmPelsWidth == capturedDevMode.dmPelsWidth) &&
|
|
(pDevmode->dmPelsHeight == capturedDevMode.dmPelsHeight) &&
|
|
(pDevmode->dmDisplayFlags == capturedDevMode.dmDisplayFlags) &&
|
|
(pDevmode->dmBitsPerPel == capturedDevMode.dmBitsPerPel)
|
|
)
|
|
{
|
|
//
|
|
// FullscreenInput->dwOrientation is 0.
|
|
//
|
|
|
|
VideoMode.RequestedMode = (ULONG) pDevmode->dmOrientation;
|
|
modeFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (modeFound == FALSE)
|
|
{
|
|
RIP("ChangeDisplaySettings: Console passed in bad DEVMODE\n");
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We have the mode number.
|
|
// Call the driver to set the mode
|
|
//
|
|
|
|
Status = GreDeviceIoControl(gFullscreenGraphicsDevice.pDeviceHandle,
|
|
IOCTL_VIDEO_SET_CURRENT_MODE,
|
|
&VideoMode,
|
|
sizeof(VideoMode),
|
|
NULL,
|
|
0,
|
|
&BytesReturned);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// We also map the memory so we can use it to
|
|
// process string commands from the console
|
|
//
|
|
|
|
VIDEO_MEMORY FrameBufferMap;
|
|
VIDEO_MEMORY_INFORMATION FrameBufferInfo;
|
|
|
|
FrameBufferMap.RequestedVirtualAddress = NULL;
|
|
|
|
Status = GreDeviceIoControl(gFullscreenGraphicsDevice.pDeviceHandle,
|
|
IOCTL_VIDEO_MAP_VIDEO_MEMORY,
|
|
&FrameBufferMap,
|
|
sizeof(FrameBufferMap),
|
|
&FrameBufferInfo,
|
|
sizeof(FrameBufferInfo),
|
|
&BytesReturned);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// get address of frame buffer
|
|
//
|
|
|
|
gpFullscreenFrameBufPtr = (PUCHAR) FrameBufferInfo.FrameBufferBase;
|
|
gpFullscreenFrameBufLength = FrameBufferInfo.FrameBufferLength;
|
|
|
|
if (gFeFullscreenGraphicsDevice.pDeviceHandle != NULL)
|
|
{
|
|
FSVIDEO_MODE_INFORMATION FsVideoMode;
|
|
//
|
|
// get current video mode
|
|
//
|
|
Status = GreDeviceIoControl(gFullscreenGraphicsDevice.pDeviceHandle,
|
|
IOCTL_VIDEO_QUERY_CURRENT_MODE,
|
|
NULL,
|
|
0,
|
|
&FsVideoMode.VideoMode,
|
|
sizeof(FsVideoMode.VideoMode),
|
|
&BytesReturned);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
FsVideoMode.VideoMemory = FrameBufferInfo;
|
|
|
|
//
|
|
// set current vide mode to full screen video driver
|
|
//
|
|
Status = GreDeviceIoControl(gFeFullscreenGraphicsDevice.pDeviceHandle,
|
|
IOCTL_FSVIDEO_SET_CURRENT_MODE,
|
|
&FsVideoMode,
|
|
sizeof(FsVideoMode),
|
|
NULL,
|
|
0,
|
|
&BytesReturned);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
}
|
|
else
|
|
{
|
|
RIP("FSVGA setmode: fullscreen MODESET failed\n");
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
RIP("Fullscreen setmode: memory mapping failed\n");
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
RIP("Fullscreen setmode: fullscreen MODESET failed\n");
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case FullscreenControlCopyFrameBuffer:
|
|
|
|
TRACE_SWITCH(("Switching: FullscreenControlCopyFrameBuffer\n"));
|
|
|
|
if ( VERIFY_RANGE(FullscreenInput, FullscreenInputLength, FrameBufLen) &&
|
|
VERIFY_RANGE(FullscreenOutput, FullscreenInputLength, FrameBufLen))
|
|
{
|
|
RtlMoveMemory(pFrameBuf + (ULONG_PTR)FullscreenOutput,
|
|
pFrameBuf + (ULONG_PTR)FullscreenInput,
|
|
FullscreenInputLength);
|
|
}
|
|
else
|
|
{
|
|
RIP("Fullscreen control - error processing input/output buffer\n");
|
|
}
|
|
break;
|
|
|
|
case FullscreenControlCopyFrameBufferDB:
|
|
|
|
TRACE_SWITCH(("Switching: FullscreenControlCopyFrameBufferDB\n"));
|
|
|
|
{
|
|
FSCNTL_SCREEN_INFO FsCntlSrc;
|
|
FSCNTL_SCREEN_INFO FsCntlDest;
|
|
ULONG_PTR offsetSrc, offsetDst, len;
|
|
|
|
__try
|
|
{
|
|
ProbeForRead(FullscreenInput, sizeof(FSCNTL_SCREEN_INFO), sizeof(USHORT));
|
|
RtlCopyMemory(&FsCntlSrc, FullscreenInput, sizeof(FSCNTL_SCREEN_INFO));
|
|
|
|
ProbeForRead(FullscreenOutput, sizeof(FSCNTL_SCREEN_INFO), sizeof(USHORT));
|
|
RtlCopyMemory(&FsCntlDest, FullscreenOutput, sizeof(FSCNTL_SCREEN_INFO));
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
RIP("Fullscreen control - error processing input/output buffer\n");
|
|
}
|
|
|
|
offsetDst = SCREEN_BUFFER_POINTER(0, FsCntlDest.Position.Y, FsCntlDest.ScreenSize.X, sizeof(VGA_CHAR));
|
|
offsetSrc = SCREEN_BUFFER_POINTER(0, FsCntlSrc.Position.Y, FsCntlSrc.ScreenSize.X, sizeof(VGA_CHAR));
|
|
len = FsCntlSrc.nNumberOfChars * sizeof(VGA_CHAR);
|
|
if ( VERIFY_RANGE(offsetDst, len, FrameBufLen) &&
|
|
VERIFY_RANGE(offsetSrc, len, FrameBufLen))
|
|
{
|
|
RtlMoveMemory(pFrameBuf + offsetDst,
|
|
pFrameBuf + offsetSrc,
|
|
len);
|
|
}
|
|
else
|
|
{
|
|
RIP("Fullscreen control - error processing input/output buffer\n");
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case FullscreenControlReadFromFrameBuffer:
|
|
|
|
TRACE_SWITCH(("Switching: FullscreenControlReadFromFrameBuffer\n"));
|
|
|
|
FullscreenInputLength = (FullscreenInputLength / 2) * 2;
|
|
if (VERIFY_RANGE(FullscreenInput, FullscreenInputLength, FrameBufLen))
|
|
{
|
|
__try
|
|
{
|
|
ProbeForWrite(FullscreenOutput, sizeof(CHAR_INFO) * FullscreenInputLength/2, sizeof(UCHAR));
|
|
|
|
pFrameBuf += (ULONG_PTR) FullscreenInput;
|
|
pCharInfo = (PCHAR_INFO) FullscreenOutput;
|
|
|
|
while (FullscreenInputLength)
|
|
{
|
|
pCharInfo->Char.AsciiChar = *pFrameBuf++;
|
|
pCharInfo->Attributes = *pFrameBuf++;
|
|
FullscreenInputLength -= 2;
|
|
pCharInfo++;
|
|
}
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
RIP("Fullscreen control - error processing input/output buffer\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RIP("Fullscreen control - error processing input/output buffer\n");
|
|
}
|
|
|
|
break;
|
|
|
|
case FullscreenControlWriteToFrameBuffer:
|
|
|
|
TRACE_SWITCH(("Switching: FullscreenControlWriteToFrameBuffer\n"));
|
|
|
|
FullscreenInputLength = (FullscreenInputLength / 4) * 4;
|
|
if (VERIFY_RANGE(FullscreenOutput, FullscreenInputLength/2, FrameBufLen))
|
|
{
|
|
__try
|
|
{
|
|
ProbeForRead(FullscreenInput, sizeof(CHAR_INFO) * FullscreenInputLength/4, sizeof(UCHAR));
|
|
|
|
pFrameBuf += (ULONG_PTR) FullscreenOutput;
|
|
pCharInfo = (PCHAR_INFO) FullscreenInput;
|
|
|
|
while (FullscreenInputLength)
|
|
{
|
|
*pFrameBuf++ = pCharInfo->Char.AsciiChar;
|
|
*pFrameBuf++ = (UCHAR) (pCharInfo->Attributes);
|
|
FullscreenInputLength -= 4;
|
|
pCharInfo++;
|
|
}
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
RIP("Fullscreen control - error processing input/output buffer\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RIP("Fullscreen control - error processing input/output buffer\n");
|
|
}
|
|
|
|
break;
|
|
|
|
case FullscreenControlWriteToFrameBufferDB:
|
|
|
|
TRACE_SWITCH(("Switching: FullscreenControlWriteToFrameBufferDB\n"));
|
|
|
|
{
|
|
FSCNTL_SCREEN_INFO FsCntl;
|
|
ULONG_PTR offset;
|
|
|
|
__try
|
|
{
|
|
ProbeForRead(FullscreenOutput, sizeof(FSCNTL_SCREEN_INFO), sizeof(USHORT));
|
|
RtlCopyMemory(&FsCntl, FullscreenOutput, sizeof(FSCNTL_SCREEN_INFO));
|
|
|
|
ProbeForRead(FullscreenInput, sizeof(CHAR_IMAGE_INFO) * FsCntl.nNumberOfChars, sizeof(USHORT));
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
RIP("Fullscreen control - error processing input/output buffer\n");
|
|
}
|
|
|
|
offset = SCREEN_BUFFER_POINTER(FsCntl.Position.X,
|
|
FsCntl.Position.Y,
|
|
FsCntl.ScreenSize.X,
|
|
sizeof(VGA_CHAR));
|
|
if (VERIFY_RANGE(offset, FsCntl.nNumberOfChars*2, FrameBufLen))
|
|
{
|
|
pFrameBuf += offset;
|
|
pCharImageInfo = (PCHAR_IMAGE_INFO) FullscreenInput;
|
|
|
|
while (FsCntl.nNumberOfChars)
|
|
{
|
|
TranslateOutputToOem(&pCharImageInfo->CharInfo,
|
|
&pCharImageInfo->CharInfo,
|
|
1);
|
|
*pFrameBuf++ = pCharImageInfo->CharInfo.Char.AsciiChar;
|
|
*pFrameBuf++ = (UCHAR) (pCharImageInfo->CharInfo.Attributes);
|
|
|
|
FsCntl.nNumberOfChars--;
|
|
pCharImageInfo++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RIP("Fullscreen control - error processing input/output buffer\n");
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case FullscreenControlReverseMousePointer:
|
|
|
|
TRACE_SWITCH(("Switching: FullscreenControlReverseMousePointer\n"));
|
|
|
|
if (VERIFY_RANGE(FullscreenInput, 1, FrameBufLen))
|
|
{
|
|
pFrameBuf += (ULONG_PTR) FullscreenInput;
|
|
|
|
Attribute = (*(pFrameBuf + 1) & 0xF0) >> 4;
|
|
Attribute |= (*(pFrameBuf + 1) & 0x0F) << 4;
|
|
*(pFrameBuf + 1) = Attribute;
|
|
}
|
|
else
|
|
{
|
|
RIP("Fullscreen control - error processing input/output buffer\n");
|
|
}
|
|
|
|
break;
|
|
|
|
case FullscreenControlReverseMousePointerDB:
|
|
|
|
TRACE_SWITCH(("Switching: FullscreenControlReverseMousePointerDB\n"));
|
|
|
|
{
|
|
FSVIDEO_REVERSE_MOUSE_POINTER MousePointer;
|
|
ULONG_PTR offset;
|
|
|
|
__try
|
|
{
|
|
ProbeForRead(FullscreenInput, sizeof(FSVIDEO_REVERSE_MOUSE_POINTER), sizeof(USHORT));
|
|
RtlCopyMemory(&MousePointer, FullscreenInput, sizeof(FSVIDEO_REVERSE_MOUSE_POINTER));
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
RIP("Fullscreen control - error processing input/output buffer\n");
|
|
}
|
|
|
|
offset = SCREEN_BUFFER_POINTER(MousePointer.Screen.Position.X,
|
|
MousePointer.Screen.Position.Y,
|
|
MousePointer.Screen.ScreenSize.X,
|
|
sizeof(VGA_CHAR));
|
|
|
|
if (VERIFY_RANGE(offset, 1, FrameBufLen))
|
|
{
|
|
pFrameBuf += offset;
|
|
Attribute = (*(pFrameBuf + 1) & 0xF0) >> 4;
|
|
Attribute |= (*(pFrameBuf + 1) & 0x0F) << 4;
|
|
*(pFrameBuf + 1) = Attribute;
|
|
}
|
|
else
|
|
{
|
|
RIP("Fullscreen control - error processing input/output buffer\n");
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
if (ioctl == IOCTL_FSVIDEO_COPY_FRAME_BUFFER)
|
|
{
|
|
PFSVIDEO_COPY_FRAME_BUFFER CopyFrameBuffer;
|
|
|
|
cCapBuffer = sizeof(FSVIDEO_COPY_FRAME_BUFFER);
|
|
pCapBuffer = PALLOCNOZ(cCapBuffer, GDITAG_FULLSCREEN);
|
|
|
|
if (!pCapBuffer)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
else
|
|
{
|
|
CopyFrameBuffer = (PFSVIDEO_COPY_FRAME_BUFFER) pCapBuffer;
|
|
__try
|
|
{
|
|
ProbeForRead(FullscreenInput, sizeof(FSCNTL_SCREEN_INFO), sizeof(USHORT));
|
|
RtlCopyMemory(&CopyFrameBuffer->SrcScreen, FullscreenInput, sizeof(FSCNTL_SCREEN_INFO));
|
|
|
|
ProbeForRead(FullscreenOutput, sizeof(FSCNTL_SCREEN_INFO), sizeof(USHORT));
|
|
RtlCopyMemory(&CopyFrameBuffer->DestScreen, FullscreenOutput, sizeof(FSCNTL_SCREEN_INFO));
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
RIP("Fullscreen control - error processing input/output buffer\n");
|
|
}
|
|
|
|
Status = GreDeviceIoControl(gFeFullscreenGraphicsDevice.pDeviceHandle,
|
|
ioctl,
|
|
pCapBuffer,
|
|
cCapBuffer,
|
|
NULL,
|
|
0,
|
|
&BytesReturned);
|
|
}
|
|
}
|
|
else
|
|
if (ioctl == IOCTL_FSVIDEO_WRITE_TO_FRAME_BUFFER)
|
|
{
|
|
PFSVIDEO_WRITE_TO_FRAME_BUFFER WriteFrameBuffer;
|
|
|
|
cCapBuffer = sizeof(FSVIDEO_WRITE_TO_FRAME_BUFFER) + FullscreenInputLength;
|
|
pCapBuffer = PALLOCNOZ(cCapBuffer, GDITAG_FULLSCREEN);
|
|
|
|
if (!pCapBuffer)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
else
|
|
{
|
|
WriteFrameBuffer = (PFSVIDEO_WRITE_TO_FRAME_BUFFER) pCapBuffer;
|
|
__try
|
|
{
|
|
ProbeForRead(FullscreenInput, FullscreenInputLength, sizeof(USHORT));
|
|
WriteFrameBuffer->SrcBuffer =
|
|
(PCHAR_IMAGE_INFO)((PBYTE)pCapBuffer + sizeof(FSVIDEO_WRITE_TO_FRAME_BUFFER));
|
|
RtlCopyMemory(WriteFrameBuffer->SrcBuffer, FullscreenInput, FullscreenInputLength);
|
|
|
|
ProbeForRead(FullscreenOutput, sizeof(FSCNTL_SCREEN_INFO), sizeof(USHORT));
|
|
RtlCopyMemory(&WriteFrameBuffer->DestScreen, FullscreenOutput, sizeof(FSCNTL_SCREEN_INFO));
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
RIP("Fullscreen control - error processing input/output buffer\n");
|
|
}
|
|
|
|
Status = GreDeviceIoControl(gFeFullscreenGraphicsDevice.pDeviceHandle,
|
|
ioctl,
|
|
pCapBuffer,
|
|
cCapBuffer,
|
|
NULL,
|
|
0,
|
|
&BytesReturned);
|
|
}
|
|
}
|
|
else
|
|
if (ioctl == IOCTL_FSVIDEO_REVERSE_MOUSE_POINTER)
|
|
{
|
|
PFSVIDEO_REVERSE_MOUSE_POINTER MouseFrameBuffer;
|
|
|
|
cCapBuffer = sizeof(FSVIDEO_REVERSE_MOUSE_POINTER);
|
|
pCapBuffer = PALLOCNOZ(cCapBuffer, GDITAG_FULLSCREEN);
|
|
|
|
if (!pCapBuffer)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
else
|
|
{
|
|
MouseFrameBuffer = (PFSVIDEO_REVERSE_MOUSE_POINTER) pCapBuffer;
|
|
__try
|
|
{
|
|
ProbeForRead(FullscreenInput, sizeof(FSVIDEO_REVERSE_MOUSE_POINTER), sizeof(USHORT));
|
|
RtlCopyMemory(MouseFrameBuffer, FullscreenInput, sizeof(FSVIDEO_REVERSE_MOUSE_POINTER));
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
RIP("Fullscreen control - error processing input/output buffer\n");
|
|
}
|
|
|
|
Status = GreDeviceIoControl(gFeFullscreenGraphicsDevice.pDeviceHandle,
|
|
ioctl,
|
|
pCapBuffer,
|
|
cCapBuffer,
|
|
NULL,
|
|
0,
|
|
&BytesReturned);
|
|
}
|
|
}
|
|
else
|
|
if (ioctl == IOCTL_FSVIDEO_SET_SCREEN_INFORMATION)
|
|
{
|
|
PFSVIDEO_SCREEN_INFORMATION ScreenInformation;
|
|
|
|
cCapBuffer = sizeof(FSVIDEO_SCREEN_INFORMATION);
|
|
pCapBuffer = PALLOCNOZ(cCapBuffer, GDITAG_FULLSCREEN);
|
|
|
|
if (!pCapBuffer)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
else
|
|
{
|
|
ScreenInformation = (PFSVIDEO_SCREEN_INFORMATION) pCapBuffer;
|
|
__try
|
|
{
|
|
ProbeForRead(FullscreenInput, sizeof(FSVIDEO_SCREEN_INFORMATION), sizeof(USHORT));
|
|
RtlCopyMemory(ScreenInformation, FullscreenInput, sizeof(FSVIDEO_SCREEN_INFORMATION));
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
RIP("Fullscreen control - error processing input/output buffer\n");
|
|
}
|
|
|
|
Status = GreDeviceIoControl(gFeFullscreenGraphicsDevice.pDeviceHandle,
|
|
ioctl,
|
|
pCapBuffer,
|
|
cCapBuffer,
|
|
NULL,
|
|
0,
|
|
&BytesReturned);
|
|
}
|
|
}
|
|
else
|
|
if (ioctl == IOCTL_FSVIDEO_SET_CURSOR_POSITION)
|
|
{
|
|
PFSVIDEO_CURSOR_POSITION FsCursorPosition;
|
|
|
|
cCapBuffer = sizeof(FSVIDEO_CURSOR_POSITION);
|
|
pCapBuffer = PALLOCNOZ(cCapBuffer, GDITAG_FULLSCREEN);
|
|
|
|
if (!pCapBuffer)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
else
|
|
{
|
|
FsCursorPosition = (PFSVIDEO_CURSOR_POSITION) pCapBuffer;
|
|
__try
|
|
{
|
|
ProbeForRead(FullscreenInput, sizeof(FSVIDEO_CURSOR_POSITION), sizeof(USHORT));
|
|
RtlCopyMemory(FsCursorPosition, FullscreenInput, sizeof(FSVIDEO_CURSOR_POSITION));
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
RIP("Fullscreen control - error processing input/output buffer\n");
|
|
}
|
|
|
|
Status = GreDeviceIoControl(gFeFullscreenGraphicsDevice.pDeviceHandle,
|
|
ioctl,
|
|
pCapBuffer,
|
|
cCapBuffer,
|
|
pCapBuffer,
|
|
cCapBuffer,
|
|
&BytesReturned);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/*
|
|
* If full screen video driver returns error,
|
|
* do calls VGA mini port driver on this.
|
|
*/
|
|
ioctl = IOCTL_VIDEO_SET_CURSOR_POSITION;
|
|
Status = GreDeviceIoControl(gFullscreenGraphicsDevice.pDeviceHandle,
|
|
ioctl,
|
|
&FsCursorPosition->Coord,
|
|
sizeof(VIDEO_CURSOR_POSITION),
|
|
&FsCursorPosition->Coord,
|
|
sizeof(VIDEO_CURSOR_POSITION),
|
|
&BytesReturned);
|
|
}
|
|
|
|
if (cCapBuffer && FullscreenOutputLength && NT_SUCCESS(Status))
|
|
{
|
|
__try
|
|
{
|
|
ProbeForWrite(FullscreenOutputLength, sizeof(ULONG), sizeof(UCHAR));
|
|
*FullscreenOutputLength = BytesReturned;
|
|
|
|
ProbeForWrite(FullscreenOutput, BytesReturned, sizeof(UCHAR));
|
|
RtlCopyMemory(FullscreenOutput, pCapBuffer, BytesReturned);
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
RIP("Fullscreen control - error processing output buffer\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// For all real operations, check the output buffer parameters
|
|
//
|
|
|
|
if ((FullscreenOutput == NULL) != (FullscreenOutputLength == NULL))
|
|
{
|
|
RIP("Fullscreen control - inconsistent output buffer information\n");
|
|
Status = STATUS_INVALID_PARAMETER_4;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We must now capture the buffers so they can be safely passed down to the
|
|
// video miniport driver
|
|
//
|
|
|
|
cCapBuffer = FullscreenInputLength;
|
|
|
|
if (FullscreenOutputLength)
|
|
{
|
|
__try
|
|
{
|
|
ProbeForRead(FullscreenOutputLength, sizeof(ULONG), sizeof(UCHAR));
|
|
cCapBuffer = max(cCapBuffer,*FullscreenOutputLength);
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
RIP("Fullscreen control - error processing input buffer\n");
|
|
}
|
|
}
|
|
|
|
if (cCapBuffer)
|
|
{
|
|
pCapBuffer = PALLOCNOZ(cCapBuffer, GDITAG_FULLSCREEN);
|
|
|
|
if (!pCapBuffer)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
else
|
|
{
|
|
__try
|
|
{
|
|
ProbeForRead(FullscreenInput, FullscreenInputLength, sizeof(UCHAR));
|
|
RtlCopyMemory(pCapBuffer, FullscreenInput, FullscreenInputLength);
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
RIP("Fullscreen control - error processing input buffer\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// For now, the IOCTL will always be sent to the VGA compatible device.
|
|
// We have a global for the handle to this device.
|
|
//
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
if (gFeFullscreenGraphicsDevice.pDeviceHandle != NULL &&
|
|
ioctl == IOCTL_VIDEO_SET_CURSOR_ATTR)
|
|
{
|
|
Status = GreDeviceIoControl(gFeFullscreenGraphicsDevice.pDeviceHandle,
|
|
ioctl,
|
|
pCapBuffer,
|
|
cCapBuffer,
|
|
pCapBuffer,
|
|
cCapBuffer,
|
|
&BytesReturned);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/*
|
|
* If full screen video driver returns error,
|
|
* do calls VGA mini port driver on this.
|
|
*/
|
|
Status = GreDeviceIoControl(gFullscreenGraphicsDevice.pDeviceHandle,
|
|
ioctl,
|
|
pCapBuffer,
|
|
cCapBuffer,
|
|
pCapBuffer,
|
|
cCapBuffer,
|
|
&BytesReturned);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
Status = GreDeviceIoControl(gFullscreenGraphicsDevice.pDeviceHandle,
|
|
ioctl,
|
|
pCapBuffer,
|
|
cCapBuffer,
|
|
pCapBuffer,
|
|
cCapBuffer,
|
|
&BytesReturned);
|
|
|
|
TRACE_SWITCH(("Switching: FullscreenControl: IOCTL status is %08lx\n",
|
|
Status));
|
|
|
|
if (cCapBuffer && FullscreenOutputLength && NT_SUCCESS(Status))
|
|
{
|
|
__try
|
|
{
|
|
*FullscreenOutputLength = BytesReturned;
|
|
|
|
ProbeForWrite(FullscreenOutput, BytesReturned, sizeof(UCHAR));
|
|
RtlCopyMemory(FullscreenOutput, pCapBuffer, BytesReturned);
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
RIP("Fullscreen control - error processing output buffer\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (pCapBuffer)
|
|
{
|
|
VFREEMEM(pCapBuffer);
|
|
}
|
|
|
|
return (Status);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* DrvLogDisplayDriverEvent
|
|
*
|
|
* We will save a piece of data in the registry so that winlogon can find
|
|
* it and put up a popup if an error occured.
|
|
*
|
|
* CRIT not needed
|
|
*
|
|
* 03-Mar-1993 andreva created
|
|
\**************************************************************************/
|
|
|
|
VOID
|
|
DrvLogDisplayDriverEvent(
|
|
DISP_DRIVER_LOG MsgType
|
|
)
|
|
{
|
|
HANDLE hkRegistry;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING RegistryPath;
|
|
UNICODE_STRING UnicodeString;
|
|
|
|
NTSTATUS Status;
|
|
DWORD dwValue = 1;
|
|
|
|
#ifdef _HYDRA_
|
|
/*
|
|
* Whatever happens on a Session, don't affect the console.
|
|
*/
|
|
if ( !G_fConsole )
|
|
return;
|
|
#endif
|
|
|
|
RtlInitUnicodeString(&UnicodeString, L"");
|
|
|
|
switch (MsgType)
|
|
{
|
|
case MsgInvalidUsingDefaultMode:
|
|
|
|
//RtlInitUnicodeString(&UnicodeString, L"DefaultMode");
|
|
break;
|
|
|
|
case MsgInvalidDisplayDriver:
|
|
|
|
//RtlInitUnicodeString(&UnicodeString, L"MissingDisplayDriver");
|
|
break;
|
|
|
|
case MsgInvalidOldDriver:
|
|
|
|
RtlInitUnicodeString(&UnicodeString, L"OldDisplayDriver");
|
|
break;
|
|
|
|
case MsgInvalidDisplay16Colors:
|
|
|
|
//RtlInitUnicodeString(&UnicodeString, L"16ColorMode");
|
|
break;
|
|
|
|
case MsgInvalidDisplayMode:
|
|
|
|
//RtlInitUnicodeString(&UnicodeString, L"BadMode");
|
|
break;
|
|
|
|
case MsgInvalidConfiguration:
|
|
|
|
//RtlInitUnicodeString(&UnicodeString, L"InvalidConfiguration");
|
|
break;
|
|
|
|
default:
|
|
|
|
WARNING("DrvLogDisplayDriverEvent: Invalid error message\n");
|
|
return;
|
|
|
|
}
|
|
|
|
if (UnicodeString.Length != 0) {
|
|
|
|
RtlInitUnicodeString(&RegistryPath,
|
|
L"\\Registry\\Machine\\System\\CurrentControlSet\\"
|
|
L"Control\\GraphicsDrivers\\InvalidDisplay");
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&RegistryPath,
|
|
OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = ZwCreateKey(&hkRegistry,
|
|
MAXIMUM_ALLOWED,
|
|
&ObjectAttributes,
|
|
0L,
|
|
NULL,
|
|
REG_OPTION_VOLATILE,
|
|
NULL);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// Write the optional data value under the key.
|
|
//
|
|
|
|
(VOID) ZwSetValueKey(hkRegistry,
|
|
&UnicodeString,
|
|
0,
|
|
REG_DWORD,
|
|
&dwValue,
|
|
sizeof(DWORD));
|
|
|
|
(VOID)ZwCloseKey(hkRegistry);
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* bEARecovery
|
|
*
|
|
* This function checks to see if the EA Recovery mechanism is enabled
|
|
* in the registry.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#define EARecovery L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Watchdog\\Display"
|
|
|
|
BOOL bEARecovery(VOID)
|
|
{
|
|
ULONG ulDefault = 0;
|
|
ULONG ulEaRecovery = 0;
|
|
ULONG ulFullRecovery = 0;
|
|
RTL_QUERY_REGISTRY_TABLE queryTable[] =
|
|
{
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT, L"EaRecovery", &ulEaRecovery, REG_DWORD, &ulDefault, sizeof(ULONG)},
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT, L"FullRecovery", &ulFullRecovery, REG_DWORD, &ulDefault, sizeof(ULONG)},
|
|
{NULL, 0, NULL}
|
|
};
|
|
|
|
RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
|
|
EARecovery,
|
|
queryTable,
|
|
NULL,
|
|
NULL);
|
|
|
|
return ulEaRecovery;
|
|
}
|
|
|
|
extern PFN WatchdogTable[INDEX_LAST];
|
|
|
|
/******************************Member*Function*****************************\
|
|
* bFillFunctionTable
|
|
*
|
|
* Fills the dispatch table called by the system with a set of routines
|
|
* which wrap the final driver entry points. This is done to allow us to
|
|
* hook each of these calls.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL bFillWatchdogTable(
|
|
PFN *Dst,
|
|
PFN *Src,
|
|
LDEVTYPE ldevType)
|
|
{
|
|
ULONG i;
|
|
|
|
//
|
|
// This is required. At least for the final entries in the
|
|
// array which are the DX entry points.
|
|
//
|
|
|
|
RtlZeroMemory(Dst, INDEX_DD_LAST * sizeof(PFN));
|
|
|
|
//
|
|
// If EA recovery is enabled, and this is a display driver then
|
|
// add the watchdog hooking code. Note, we really don't want to
|
|
// hook remote drivers such as RDPDD. Unfortunately we don't know
|
|
// what driver is a remote driver and what isn't. There is a
|
|
// GCAPS2_REMOTEDRIVER flag that indicates this at DrvEnablePDEV time,
|
|
// but that is really to late.
|
|
//
|
|
// Fortunately this flag currently just indicates whether or not it
|
|
// is safe to hook DrvNineGrid. So I could just check for the existance
|
|
// of the DrvNineGrid function, and use that as a "remote driver" flag.
|
|
//
|
|
// Theoretically no "real" display driver is supposed to hook this
|
|
// function, so we shouldn't have to worry about a non-remote driver
|
|
// losing EA recovery support.
|
|
//
|
|
|
|
if (bEARecovery() &&
|
|
(ldevType == LDEV_DEVICE_DISPLAY) &&
|
|
(Src[INDEX_DrvNineGrid] == NULL)) {
|
|
|
|
for (i=0; i<INDEX_LAST; i++) {
|
|
|
|
if (*Src && WatchdogTable[i]) {
|
|
*Dst = WatchdogTable[i];
|
|
} else {
|
|
*Dst = *Src;
|
|
}
|
|
|
|
Src++;
|
|
Dst++;
|
|
}
|
|
|
|
} else {
|
|
|
|
RtlCopyMemory(Dst, Src, INDEX_LAST * sizeof(PFN));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* bFillFunctionTable
|
|
*
|
|
* Fills the dispatch table of the LDEV with function pointers from the
|
|
* driver.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL bFillFunctionTable(
|
|
PDRVFN pdrvfn,
|
|
ULONG cdrvfn,
|
|
PFN* ppfnTable)
|
|
{
|
|
//
|
|
// fill with zero pointers to avoid possibility of accessing
|
|
// incorrect fields later
|
|
//
|
|
|
|
RtlZeroMemory(ppfnTable, INDEX_LAST*sizeof(PFN));
|
|
|
|
//
|
|
// Copy driver functions into our table.
|
|
//
|
|
|
|
while (cdrvfn--)
|
|
{
|
|
//
|
|
// Check the range of the index.
|
|
//
|
|
|
|
if (pdrvfn->iFunc >= INDEX_LAST)
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Copy the pointer.
|
|
//
|
|
|
|
ppfnTable[pdrvfn->iFunc] = pdrvfn->pfn;
|
|
pdrvfn++;
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* ldevbFillTable (ded)
|
|
*
|
|
* Fills the dispatch table of the LDEV with function pointers from the
|
|
* driver. Checks that the required functions are present.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
static const ULONG aiFuncRequired[] =
|
|
{
|
|
INDEX_DrvEnablePDEV,
|
|
INDEX_DrvCompletePDEV,
|
|
INDEX_DrvDisablePDEV,
|
|
};
|
|
|
|
static const ULONG aiFuncPairs[][2] =
|
|
{
|
|
{INDEX_DrvCreateDeviceBitmap, INDEX_DrvDeleteDeviceBitmap},
|
|
{INDEX_DrvMovePointer, INDEX_DrvSetPointerShape}
|
|
};
|
|
|
|
static const ULONG aiFuncRequiredFD[] =
|
|
{
|
|
INDEX_DrvQueryFont,
|
|
INDEX_DrvQueryFontTree,
|
|
INDEX_DrvQueryFontData,
|
|
INDEX_DrvQueryFontCaps,
|
|
INDEX_DrvLoadFontFile,
|
|
INDEX_DrvUnloadFontFile,
|
|
INDEX_DrvQueryFontFile
|
|
};
|
|
|
|
BOOL
|
|
ldevFillTable(
|
|
PLDEV pldev,
|
|
DRVENABLEDATA *pded,
|
|
LDEVTYPE ldt)
|
|
{
|
|
//
|
|
// Get local copies of ded info and a pointer to the dispatch table.
|
|
//
|
|
|
|
ULONG cLeft = pded->c;
|
|
PDRVFN pdrvfn = pded->pdrvfn;
|
|
PFN *ppfnTable = pldev->apfnDriver;
|
|
|
|
//
|
|
// Store the driver version in the LDEV
|
|
//
|
|
|
|
pldev->ulDriverVersion = pded->iDriverVersion;
|
|
|
|
if (!bFillFunctionTable(pdrvfn, cLeft, ppfnTable))
|
|
{
|
|
ASSERTGDI(FALSE,"ldevFillTable: bogus function index\n");
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Check for required driver functions.
|
|
//
|
|
|
|
cLeft = sizeof(aiFuncRequired) / sizeof(ULONG);
|
|
while (cLeft--)
|
|
{
|
|
if (ppfnTable[aiFuncRequired[cLeft]] == (PFN) NULL)
|
|
{
|
|
ASSERTGDI(FALSE,"ldevFillTable: a required function is missing from driver\n");
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for required font functions.
|
|
//
|
|
|
|
if (pldev->ldevType == LDEV_FONT)
|
|
{
|
|
cLeft = sizeof(aiFuncRequiredFD) / sizeof(ULONG);
|
|
while (cLeft--)
|
|
{
|
|
if (ppfnTable[aiFuncRequiredFD[cLeft]] == (PFN) NULL)
|
|
{
|
|
ASSERTGDI(FALSE,"FillTable(): a required FD function is missing\n");
|
|
return(FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for functions that come in pairs.
|
|
//
|
|
|
|
cLeft = sizeof(aiFuncPairs) / sizeof(ULONG) / 2;
|
|
while (cLeft--)
|
|
{
|
|
//
|
|
// Make sure that either both functions are hooked or both functions
|
|
// are not hooked.
|
|
//
|
|
|
|
if ((ppfnTable[aiFuncPairs[cLeft][0]] == (PFN) NULL)
|
|
!= (ppfnTable[aiFuncPairs[cLeft][1]] == (PFN) NULL))
|
|
{
|
|
ASSERTGDI(FALSE,"ldevFillTable: one of pair of functions is missing from driver\n");
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// The driver supplied function table looks good. Create another
|
|
// table which mirrors this one with new functions that can add
|
|
// monitoring code.
|
|
//
|
|
|
|
if (!bFillWatchdogTable((PFN*)&pldev->apfn, (PFN*)&pldev->apfnDriver, ldt)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* ldevLoadInternal
|
|
*
|
|
* Enable one of the statically linked font drivers via the LDEV.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
PLDEV
|
|
ldevLoadInternal(
|
|
PFN pfnEnable,
|
|
LDEVTYPE ldt)
|
|
{
|
|
GDIFunctionID(ldevLoadInternal);
|
|
|
|
PLDEV pldev;
|
|
|
|
TRACE_INIT(("ldevLoadInternal ENTERING\n"));
|
|
|
|
GreAcquireSemaphoreEx(ghsemDriverMgmt, SEMORDER_DRIVERMGMT, NULL);
|
|
|
|
//
|
|
// Allocate memory for the LDEV.
|
|
//
|
|
|
|
pldev = (PLDEV) PALLOCMEM(sizeof(LDEV), GDITAG_LDEV);
|
|
|
|
if (pldev)
|
|
{
|
|
//
|
|
// Call the Enable entry point.
|
|
//
|
|
|
|
DRVENABLEDATA ded;
|
|
|
|
if ((!((* (PFN_DrvEnableDriver) pfnEnable) (ENGINE_VERSION,
|
|
sizeof(DRVENABLEDATA),
|
|
&ded))) ||
|
|
(!ldevFillTable(pldev, &ded, ldt)))
|
|
{
|
|
VFREEMEM(pldev);
|
|
pldev = NULL;
|
|
}
|
|
else
|
|
{
|
|
pldev->ldevType = ldt;
|
|
pldev->cldevRefs = 1;
|
|
pldev->bThreadStuck = FALSE;
|
|
|
|
//
|
|
// Initialize the rest of the LDEV.
|
|
//
|
|
|
|
if (gpldevDrivers)
|
|
{
|
|
gpldevDrivers->pldevPrev = pldev;
|
|
}
|
|
|
|
pldev->pldevNext = gpldevDrivers;
|
|
pldev->pldevPrev = NULL;
|
|
|
|
gpldevDrivers = pldev;
|
|
|
|
//
|
|
// Since this driver is statically linked in, there is no name or
|
|
// MODOBJ.
|
|
//
|
|
|
|
pldev->pGdiDriverInfo = NULL;
|
|
|
|
TRACE_INIT(("ldevLoadInternal: SUCCESS loaded static driver (font or DDML)\n"));
|
|
}
|
|
}
|
|
|
|
GreReleaseSemaphoreEx(ghsemDriverMgmt);
|
|
|
|
return pldev;
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* ldevLoadImage
|
|
*
|
|
* Examines the list of loaded drivers and returns library handles (if any).
|
|
*
|
|
* Updates the ref cnt on driver usage.
|
|
*
|
|
* If the driver is not already loaded, a node is created and the dll is
|
|
* loaded in the kernel.
|
|
\**************************************************************************/
|
|
|
|
PLDEV
|
|
ldevLoadImage(
|
|
LPWSTR pwszDriver,
|
|
BOOL bImage,
|
|
PBOOL pbAlreadyLoaded,
|
|
BOOL LoadInSessionSpace)
|
|
{
|
|
|
|
PSYSTEM_GDI_DRIVER_INFORMATION pGdiDriverInfo = NULL;
|
|
PLDEV pldev = NULL;
|
|
|
|
UNICODE_STRING usDriverName;
|
|
PLDEV pldevList;
|
|
|
|
NTSTATUS Status;
|
|
BOOLEAN OldHardErrorMode;
|
|
BOOL bSearchAgain, bLoadAgain;
|
|
BOOL bResetDriverNameToSystem32;
|
|
|
|
|
|
TRACE_INIT(("ldevLoadImage called on Image %ws\n", pwszDriver));
|
|
|
|
*pbAlreadyLoaded = FALSE;
|
|
|
|
//
|
|
// Only append the .dll if it's NOT an image.
|
|
//
|
|
|
|
if (MakeSystemRelativePath(pwszDriver,
|
|
&usDriverName,
|
|
!bImage))
|
|
{
|
|
bSearchAgain = TRUE;
|
|
|
|
searchagain:
|
|
|
|
//
|
|
// Check both lists of drivers.
|
|
//
|
|
|
|
pldevList = gpldevDrivers;
|
|
|
|
TRACE_INIT(("ldevLoadImage - search for existing image %ws\n",
|
|
usDriverName.Buffer));
|
|
|
|
while (pldevList != NULL)
|
|
{
|
|
//
|
|
// If there is a valid driver image, and if the types are compatible.
|
|
// bImage == TRUE means load an image, while bImage == FALSE means
|
|
// anything else (for now)
|
|
//
|
|
|
|
if ((pldevList->pGdiDriverInfo) &&
|
|
((pldevList->ldevType == LDEV_IMAGE) == bImage))
|
|
{
|
|
//
|
|
// Do a case insensitive compare since the printer driver name
|
|
// can come from different locations.
|
|
//
|
|
|
|
if (RtlEqualUnicodeString(&(pldevList->pGdiDriverInfo->DriverName),
|
|
&usDriverName,
|
|
TRUE))
|
|
{
|
|
//
|
|
// If it's already loaded, increment the ref count
|
|
// and return that pointer.
|
|
//
|
|
|
|
TRACE_INIT(("ldevLoadImage found image. Inc ref count\n"));
|
|
|
|
pldevList->cldevRefs += 1;
|
|
|
|
*pbAlreadyLoaded = TRUE;
|
|
|
|
pldev = pldevList;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pldevList = pldevList->pldevNext;
|
|
}
|
|
|
|
if (pldev == NULL)
|
|
{
|
|
if (!LoadInSessionSpace)
|
|
{
|
|
// If not a session load see if the Driver is from the
|
|
// \SystemRoot\System32\Drivers subdir. Ex dxapi.sys
|
|
if (bSearchAgain)
|
|
{
|
|
bSearchAgain = FALSE;
|
|
PWSTR pOldBuffer = usDriverName.Buffer;
|
|
bResetDriverNameToSystem32 = FALSE;
|
|
if (MakeSystemDriversRelativePath(pwszDriver,
|
|
&usDriverName,
|
|
!bImage))
|
|
{
|
|
bResetDriverNameToSystem32 = TRUE;
|
|
VFREEMEM(pOldBuffer);
|
|
goto searchagain;
|
|
}
|
|
}
|
|
|
|
if (bResetDriverNameToSystem32)
|
|
{
|
|
PWSTR pOldBuffer = usDriverName.Buffer;
|
|
if (!MakeSystemRelativePath(pwszDriver,
|
|
&usDriverName,
|
|
!bImage))
|
|
{
|
|
goto done;
|
|
}
|
|
VFREEMEM(pOldBuffer);
|
|
}
|
|
}
|
|
|
|
TRACE_INIT(("ldevLoadImage - attempting to load new image\n"));
|
|
|
|
pGdiDriverInfo = (PSYSTEM_GDI_DRIVER_INFORMATION)
|
|
PALLOCNOZ(sizeof(SYSTEM_GDI_DRIVER_INFORMATION), GDITAG_LDEV);
|
|
|
|
pldev = (PLDEV) PALLOCMEM(sizeof(LDEV), GDITAG_LDEV);
|
|
|
|
bLoadAgain = TRUE;
|
|
|
|
if (pGdiDriverInfo && pldev)
|
|
{
|
|
loadagain:
|
|
pGdiDriverInfo->DriverName = usDriverName;
|
|
|
|
if (LoadInSessionSpace) {
|
|
Status = ZwSetSystemInformation(SystemLoadGdiDriverInformation,
|
|
pGdiDriverInfo,
|
|
sizeof(SYSTEM_GDI_DRIVER_INFORMATION));
|
|
} else {
|
|
Status = ZwSetSystemInformation(SystemLoadGdiDriverInSystemSpace,
|
|
pGdiDriverInfo,
|
|
sizeof(SYSTEM_GDI_DRIVER_INFORMATION));
|
|
}
|
|
|
|
//
|
|
// If we get an OBJECT_NAME_NOT_FOUND we handle it the
|
|
// following way:
|
|
//
|
|
// (1) If its a session load then its an error.
|
|
//
|
|
// (2) If it is a non session load then we try again to load
|
|
// the driver from System32\Drivers subdirectory. We do
|
|
// this because DX can ask us to load dxapi.sys and
|
|
// it lives in drivers subdir.
|
|
//
|
|
// In general we should never get IMAGE_ALREADY_LOADED because
|
|
// we check for loaded images above.
|
|
//
|
|
// If we do get this status we handle them in the following
|
|
// manner:
|
|
//
|
|
// (1) If we get IMAGE_ALREADY_LOADED when LoadInSessionSpace
|
|
// is true it is an error. We fail the ldev creation. Why ?
|
|
// because the image may be loaded in another session
|
|
// space and not in ours.
|
|
//
|
|
// (2) If we get IMAGE_ALREADY_LOADED when LoadInSessionSpace
|
|
// is false (i.e load in non session space) we will fail
|
|
// to create the ldev for modules (dll/sys) that win32k.sys
|
|
// does not statically link to. Why ? because such modules
|
|
// can get unloaded without win32k.sys knowing about it.
|
|
// By makeing sure we succeed only statically linked
|
|
// modules we are gauranteed they wont get unloaded
|
|
// behind our back because of our static link dependancy.
|
|
// This also means for such ldevs we cant unload them
|
|
// in ldevUnloadImage's ZwSetSystemInformation. A
|
|
// bStaticImportLink BOOL has been added to the LDEV to
|
|
// handle this case.
|
|
//
|
|
|
|
if ((NT_SUCCESS( Status )))
|
|
{
|
|
addstaticimport:
|
|
TRACE_INIT(("ldevLoadImage SUCCESS with HANDLE %08lx\n",
|
|
(ULONG_PTR)pGdiDriverInfo));
|
|
|
|
pldev->pGdiDriverInfo = pGdiDriverInfo;
|
|
pldev->bArtificialIncrement = FALSE;
|
|
pldev->cldevRefs = 1;
|
|
pldev->bThreadStuck = FALSE;
|
|
|
|
// Assume image for now.
|
|
|
|
pldev->ldevType = LDEV_IMAGE;
|
|
|
|
pldev->ulDriverVersion = (ULONG) -1;
|
|
|
|
if (gpldevDrivers)
|
|
{
|
|
gpldevDrivers->pldevPrev = pldev;
|
|
}
|
|
|
|
pldev->pldevNext = gpldevDrivers;
|
|
pldev->pldevPrev = NULL;
|
|
|
|
gpldevDrivers = pldev;
|
|
|
|
//
|
|
// We exit with all resources allocated, after leaving the
|
|
// semaphore.
|
|
//
|
|
|
|
return (pldev);
|
|
}
|
|
else
|
|
{
|
|
if (!LoadInSessionSpace)
|
|
{
|
|
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
|
|
{
|
|
if(bLoadAgain)
|
|
{
|
|
bLoadAgain = FALSE;
|
|
|
|
PWSTR pOldBuffer = usDriverName.Buffer;
|
|
if (MakeSystemDriversRelativePath(pwszDriver,
|
|
&usDriverName,
|
|
!bImage))
|
|
{
|
|
VFREEMEM(pOldBuffer);
|
|
goto loadagain;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Status == STATUS_IMAGE_ALREADY_LOADED)
|
|
{
|
|
//
|
|
// We allow this load to work only if the module is
|
|
// in win32k.sys static import lib list.
|
|
//
|
|
|
|
RTL_PROCESS_MODULES ModuleInformation;
|
|
DWORD cbModuleInformation;
|
|
DWORD ReturnedLength;
|
|
PRTL_PROCESS_MODULES pModuleInformation = 0;
|
|
DWORD i;
|
|
ANSI_STRING asDriver;
|
|
UNICODE_STRING uTmp;
|
|
BOOL bFoundDriver = FALSE;
|
|
BOOL bFreeAsDriver = FALSE;
|
|
WCHAR *pwszDriverFileName = 0;
|
|
|
|
pwszDriverFileName = wcsrchr(pwszDriver,L'\\');
|
|
|
|
if (pwszDriverFileName)
|
|
pwszDriverFileName++;
|
|
else
|
|
pwszDriverFileName = pwszDriver;
|
|
|
|
RtlInitUnicodeString(&uTmp, pwszDriverFileName);
|
|
|
|
Status = RtlUnicodeStringToAnsiString(&asDriver,&uTmp,TRUE);
|
|
|
|
//
|
|
// 1) Locate asDriver name in system Module list:
|
|
//
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
bFreeAsDriver = TRUE;
|
|
|
|
Status = ZwQuerySystemInformation(SystemModuleInformation,
|
|
&ModuleInformation,
|
|
sizeof(ModuleInformation),
|
|
&ReturnedLength);
|
|
if (NT_SUCCESS(Status) || (Status == STATUS_INFO_LENGTH_MISMATCH))
|
|
{
|
|
cbModuleInformation = offsetof(RTL_PROCESS_MODULES, Modules);
|
|
cbModuleInformation += ModuleInformation.NumberOfModules * sizeof(RTL_PROCESS_MODULE_INFORMATION);
|
|
|
|
pModuleInformation = (PRTL_PROCESS_MODULES)PALLOCNOZ(cbModuleInformation, 'pmtG');
|
|
|
|
if (pModuleInformation)
|
|
{
|
|
Status = ZwQuerySystemInformation(SystemModuleInformation,
|
|
pModuleInformation,
|
|
cbModuleInformation,
|
|
&ReturnedLength);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
for (i = 0; i < ModuleInformation.NumberOfModules; i++)
|
|
{
|
|
CHAR *FullPathName = (CHAR*)(pModuleInformation->Modules[i].FullPathName);
|
|
USHORT OffsetToFileName = pModuleInformation->Modules[i].OffsetToFileName;
|
|
|
|
if(!_strnicmp(&FullPathName[OffsetToFileName],
|
|
asDriver.Buffer,
|
|
asDriver.Length))
|
|
{
|
|
bFoundDriver = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// 2) Found it in System Module list? then locate it in win32k.sys static import list
|
|
//
|
|
|
|
if (bFoundDriver)
|
|
{
|
|
bFoundDriver = FALSE;
|
|
|
|
PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
|
|
ULONG ImportSize;
|
|
PSZ ImportName;
|
|
|
|
ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)
|
|
RtlImageDirectoryEntryToData(gpvWin32kImageBase,
|
|
TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_IMPORT,
|
|
&ImportSize);
|
|
while (ImportDescriptor &&
|
|
ImportDescriptor->Name &&
|
|
ImportDescriptor->OriginalFirstThunk)
|
|
{
|
|
ImportName = (PSZ)((PCHAR)gpvWin32kImageBase +
|
|
ImportDescriptor->Name);
|
|
|
|
if (!_strnicmp(ImportName,
|
|
asDriver.Buffer,
|
|
asDriver.Length))
|
|
{
|
|
bFoundDriver = TRUE;
|
|
break;
|
|
}
|
|
|
|
ImportDescriptor += 1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 3) Found it in both system Module list and win32k.sys static import list ?
|
|
// then gather GDISystemInformation and add pldev to gpLdevList:
|
|
|
|
if (bFoundDriver)
|
|
{
|
|
PVOID ImageBaseAddress = pModuleInformation->Modules[i].ImageBase;
|
|
ULONG_PTR EntryPoint;
|
|
ULONG Size;
|
|
|
|
pGdiDriverInfo->ExportSectionPointer = (PIMAGE_EXPORT_DIRECTORY)
|
|
RtlImageDirectoryEntryToData(ImageBaseAddress,
|
|
TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_EXPORT,
|
|
&Size);
|
|
|
|
PIMAGE_NT_HEADERS NtHeaders = RtlImageNtHeader(ImageBaseAddress);
|
|
EntryPoint = NtHeaders->OptionalHeader.AddressOfEntryPoint;
|
|
EntryPoint += (ULONG_PTR)ImageBaseAddress;
|
|
|
|
pGdiDriverInfo->ImageAddress = ImageBaseAddress;
|
|
pGdiDriverInfo->SectionPointer = 0;
|
|
pGdiDriverInfo->EntryPoint = (PVOID)EntryPoint;
|
|
|
|
}
|
|
|
|
if (pModuleInformation)
|
|
VFREEMEM(pModuleInformation);
|
|
if (bFreeAsDriver)
|
|
RtlFreeAnsiString(&asDriver);
|
|
|
|
if (bFoundDriver)
|
|
{
|
|
pldev->bStaticImportLink = TRUE;
|
|
goto addstaticimport;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If MmLoadSystemImage failed to load the driver because
|
|
// of unresolved imports, this is likely an old driver
|
|
// being linked against something other than win32k.sys.
|
|
// Call user to log the error.
|
|
|
|
if (Status == STATUS_PROCEDURE_NOT_FOUND)
|
|
{
|
|
DrvLogDisplayDriverEvent(MsgInvalidOldDriver);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Either success due to a cached entry, or failure.
|
|
// In either case, we can free all the resources we allocatred.
|
|
//
|
|
|
|
if (pGdiDriverInfo)
|
|
VFREEMEM(pGdiDriverInfo);
|
|
|
|
if (pldev)
|
|
VFREEMEM(pldev);
|
|
|
|
pldev = NULL;
|
|
|
|
}
|
|
done:
|
|
VFREEMEM(usDriverName.Buffer);
|
|
}
|
|
|
|
TRACE_INIT(("ldevLoadImage %ws with HANDLE %08lx\n",
|
|
pldev ? L"SUCCESS" : L"FAILED", pldev));
|
|
|
|
return (pldev);
|
|
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* ldevUnloadImage()
|
|
*
|
|
* Decrements the refcnt on driver usage.
|
|
* If the refcnt is zero {
|
|
* Deletes an LDEV. Disables and unloads the driver.
|
|
* }
|
|
\**************************************************************************/
|
|
|
|
VOID
|
|
ldevUnloadImage(
|
|
PLDEV pldev)
|
|
{
|
|
GDIFunctionID(ldevUnloadImage);
|
|
|
|
LPWSTR pDriverFile = NULL, pCopyDriverFile = NULL;
|
|
ULONG cbDriverFile;
|
|
|
|
//
|
|
// Hold the LDEV semaphore until after the module is unloaded.
|
|
//
|
|
|
|
GreAcquireSemaphoreEx(ghsemDriverMgmt, SEMORDER_DRIVERMGMT, NULL);
|
|
|
|
if (--pldev->cldevRefs == 0)
|
|
{
|
|
//
|
|
// Make sure that there is exactly one reference to this LDEV.
|
|
//
|
|
|
|
TRACE_INIT(("ldevUnloadImage: ENTERING\n"));
|
|
|
|
//
|
|
// Call the driver unload routine if it exists.
|
|
//
|
|
|
|
PFN_DrvDisableDriver pfnDisableDriver =
|
|
(PFN_DrvDisableDriver) pldev->apfn[INDEX_DrvDisableDriver];
|
|
|
|
if (pfnDisableDriver)
|
|
{
|
|
(*pfnDisableDriver)();
|
|
}
|
|
|
|
//
|
|
// If the module handle exists, we need to unload the module. (Does not exist
|
|
// for the statically linked font drivers).
|
|
//
|
|
|
|
if (pldev->pGdiDriverInfo)
|
|
{
|
|
//
|
|
// Tell the module to unload.
|
|
//
|
|
|
|
TRACE_INIT(("ldevUnloadImage called on Image %08lx, %ws\n",
|
|
(ULONG_PTR) pldev, pldev->pGdiDriverInfo->DriverName.Buffer));
|
|
|
|
if (!(pldev->bStaticImportLink))
|
|
{
|
|
ZwSetSystemInformation(SystemUnloadGdiDriverInformation,
|
|
&(pldev->pGdiDriverInfo->SectionPointer),
|
|
sizeof(ULONG_PTR));
|
|
}
|
|
|
|
//
|
|
// If a printer driver is being unloaded, the spooler must be informed
|
|
// so that it may perform driver upgrades.
|
|
//
|
|
|
|
if ((pldev->ldevType == LDEV_DEVICE_PRINTER) &&
|
|
(pldev->bArtificialIncrement == FALSE))
|
|
{
|
|
// Copy the driver file name into a temp buffer
|
|
// This will be used to notify the spooler of the driver unload
|
|
// from outside the semaphore
|
|
pDriverFile = pldev->pGdiDriverInfo->DriverName.Buffer;
|
|
|
|
// Check for invalid printer driver names.
|
|
if (pDriverFile && *pDriverFile)
|
|
{
|
|
// Copy the driver name into another buffer.
|
|
cbDriverFile = (wcslen(pDriverFile) + 1) * sizeof(WCHAR);
|
|
pCopyDriverFile = (LPWSTR) PALLOCMEM(cbDriverFile, 'lpsG');
|
|
|
|
if (pCopyDriverFile) {
|
|
memcpy(pCopyDriverFile, pDriverFile, cbDriverFile);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Remove the ldev from the linker list.
|
|
//
|
|
|
|
if (pldev->pldevNext)
|
|
{
|
|
pldev->pldevNext->pldevPrev = pldev->pldevPrev;
|
|
}
|
|
|
|
if (pldev->pldevPrev)
|
|
{
|
|
pldev->pldevPrev->pldevNext = pldev->pldevNext;
|
|
}
|
|
else
|
|
{
|
|
gpldevDrivers = pldev->pldevNext;
|
|
}
|
|
|
|
GreReleaseSemaphoreEx(ghsemDriverMgmt);
|
|
|
|
//
|
|
// Free the ldev
|
|
//
|
|
|
|
if (pldev->pGdiDriverInfo)
|
|
{
|
|
//
|
|
// Free the memory associated with the module.
|
|
//
|
|
|
|
VFREEMEM(pldev->pGdiDriverInfo->DriverName.Buffer);
|
|
VFREEMEM(pldev->pGdiDriverInfo);
|
|
|
|
}
|
|
VFREEMEM(pldev);
|
|
}
|
|
else
|
|
{
|
|
TRACE_INIT(("ldevUnloadImage - refcount decremented\n"));
|
|
GreReleaseSemaphoreEx(ghsemDriverMgmt);
|
|
}
|
|
|
|
if (pCopyDriverFile)
|
|
{
|
|
GrePrinterDriverUnloadW(pCopyDriverFile);
|
|
VFREEMEM(pCopyDriverFile);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* ldevLoadDriver
|
|
*
|
|
* Locate an existing driver or load a new one. Increase its reference
|
|
* count.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
PLDEV
|
|
ldevLoadDriver(
|
|
LPWSTR pwszDriver,
|
|
LDEVTYPE ldt)
|
|
{
|
|
GDIFunctionID(ldevLoadDriver);
|
|
|
|
NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
PLDEV pldev;
|
|
BOOL bLoaded;
|
|
|
|
|
|
|
|
//
|
|
// Check for a bogus driver name.
|
|
//
|
|
|
|
if ((pwszDriver == NULL) ||
|
|
(*pwszDriver == L'\0'))
|
|
{
|
|
WARNING("ldevLoadDriver: bogus driver name\n");
|
|
return NULL;
|
|
}
|
|
|
|
#if DBG
|
|
//
|
|
// Check for bogus driver type
|
|
//
|
|
|
|
if ((ldt != LDEV_FONT) &&
|
|
(ldt != LDEV_DEVICE_DISPLAY) &&
|
|
(ldt != LDEV_DEVICE_PRINTER) &&
|
|
(ldt != LDEV_DEVICE_MIRROR))
|
|
{
|
|
WARNING("ldevLoadDriver: bad LDEVTYPE\n");
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
GreAcquireSemaphoreEx(ghsemDriverMgmt, SEMORDER_DRIVERMGMT, NULL);
|
|
|
|
pldev = ldevLoadImage(pwszDriver, FALSE, &bLoaded, TRUE);
|
|
|
|
if (pldev)
|
|
{
|
|
if (bLoaded)
|
|
{
|
|
TRACE_INIT(("ldevLoadDriver: SUCCESS, Driver already loaded\n"));
|
|
|
|
GreReleaseSemaphoreEx(ghsemDriverMgmt);
|
|
}
|
|
else
|
|
{
|
|
DRVENABLEDATA ded = {0,0,(DRVFN *) NULL};
|
|
|
|
if ((pldev->pGdiDriverInfo->EntryPoint != NULL) &&
|
|
((PFN_DrvEnableDriver) pldev->pGdiDriverInfo->EntryPoint)(
|
|
ENGINE_VERSION, sizeof(DRVENABLEDATA), &ded) &&
|
|
(ded.iDriverVersion <= ENGINE_VERSION) &&
|
|
(ded.iDriverVersion >= ENGINE_VERSIONSUR) &&
|
|
ldevFillTable(pldev, &ded, ldt))
|
|
{
|
|
|
|
//
|
|
// Make sure the name and type of the ldev is initialized
|
|
//
|
|
|
|
pldev->ldevType = ldt;
|
|
|
|
//
|
|
// For printer drivers increment the refcnt to 2. This will keep
|
|
// the drivers loaded until they are upgraded by the spooler. The
|
|
// artificial increment will be undone when the spooler sends the
|
|
// appropriate message.
|
|
//
|
|
|
|
if (ldt == LDEV_DEVICE_PRINTER)
|
|
{
|
|
if (!pldev->bArtificialIncrement)
|
|
{
|
|
pldev->cldevRefs += 1;
|
|
pldev->bArtificialIncrement = TRUE;
|
|
}
|
|
}
|
|
|
|
GreReleaseSemaphoreEx(ghsemDriverMgmt);
|
|
|
|
#ifdef _HYDRA_
|
|
/*
|
|
* For remote video driver, call connect entry point to pass the
|
|
* Client data and the thinwire cache data area pointer.
|
|
*
|
|
* Only do this once. We don't support multiple drivers.
|
|
*/
|
|
|
|
PFN_DrvConnect pConnectCall;
|
|
|
|
pConnectCall = (PFN_DrvConnect)((pldev->apfn[INDEX_DrvConnect]));
|
|
|
|
if ( pConnectCall ) {
|
|
ASSERT(gProtocolType != PROTOCOL_DISCONNECT);
|
|
|
|
BOOL Result;
|
|
|
|
Result = (*pConnectCall) (
|
|
G_RemoteConnectionChannel,
|
|
G_RemoteConnectionFileObject,
|
|
G_RemoteVideoFileObject,
|
|
G_PerformanceStatistics );
|
|
if ( !Result ) {
|
|
//
|
|
// Error exit path
|
|
//
|
|
|
|
ldevUnloadImage(pldev);
|
|
|
|
pldev = NULL;
|
|
|
|
TRACE_INIT(("LDEVREF::LDEVREF: CONNECT FAILIURE\n"));
|
|
return pldev;
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
TRACE_INIT(("ldevLoadDriver: SUCCESS\n"));
|
|
}
|
|
else
|
|
{
|
|
GreReleaseSemaphoreEx(ghsemDriverMgmt);
|
|
|
|
//
|
|
// Error exit path
|
|
//
|
|
|
|
ldevUnloadImage(pldev);
|
|
|
|
pldev = NULL;
|
|
|
|
TRACE_INIT(("ldevLoadDriver: FAILURE\n"));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GreReleaseSemaphoreEx(ghsemDriverMgmt);
|
|
}
|
|
|
|
return pldev;
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* ldevArtificialDecrement
|
|
*
|
|
* Removes the artificial increment on printer drivers.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL
|
|
ldevArtificialDecrement(
|
|
LPWSTR pwszDriver)
|
|
{
|
|
GDIFunctionID(ldevArtificialDecrement);
|
|
|
|
PLDEV pldev = NULL, pldevList;
|
|
BOOL bReturn = FALSE;
|
|
UNICODE_STRING usDriverName;
|
|
|
|
PSYSTEM_GDI_DRIVER_INFORMATION pGdiDriverInfo = NULL;
|
|
|
|
//
|
|
// Check for invalid driver name.
|
|
//
|
|
|
|
if (!pwszDriver || !*pwszDriver)
|
|
{
|
|
return bReturn;
|
|
}
|
|
|
|
TRACE_INIT(("ldevArtificialDecrement called on Image %ws\n", pwszDriver));
|
|
|
|
//
|
|
// Append .dll for printer drivers
|
|
//
|
|
|
|
if (MakeSystemRelativePath(pwszDriver,
|
|
&usDriverName,
|
|
TRUE))
|
|
{
|
|
//
|
|
// Check both list of drivers.
|
|
//
|
|
|
|
GreAcquireSemaphoreEx(ghsemDriverMgmt, SEMORDER_DRIVERMGMT, NULL);
|
|
|
|
pldevList = gpldevDrivers;
|
|
|
|
TRACE_INIT(("ldevArtificialDecrement - search for existing image %ws\n",
|
|
usDriverName.Buffer));
|
|
|
|
while (pldevList != NULL)
|
|
{
|
|
//
|
|
// Check for loaded printer drivers.
|
|
//
|
|
|
|
if ((pldevList->pGdiDriverInfo) &&
|
|
(pldevList->ldevType == LDEV_DEVICE_PRINTER))
|
|
{
|
|
//
|
|
// Do a case insensitive compare since the printer driver name
|
|
// can come from different locations.
|
|
//
|
|
|
|
if (RtlEqualUnicodeString(&(pldevList->pGdiDriverInfo->DriverName),
|
|
&usDriverName,
|
|
TRUE))
|
|
{
|
|
//
|
|
// If the driver is found and has an artificial increment on it,
|
|
// call ldevUnloadImage once and reset the bArtificialIncrement
|
|
// flag.
|
|
//
|
|
|
|
TRACE_INIT(("ldevArtificialIncrement found the driver.\n"));
|
|
|
|
if (pldevList->bArtificialIncrement)
|
|
{
|
|
pldev = pldevList;
|
|
pldev->bArtificialIncrement = FALSE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
pldevList = pldevList->pldevNext;
|
|
}
|
|
|
|
|
|
GreReleaseSemaphoreEx(ghsemDriverMgmt);
|
|
|
|
VFREEMEM(usDriverName.Buffer);
|
|
}
|
|
|
|
if (pldev)
|
|
{
|
|
// Spooler will be sent a message when the driver is unloaded.
|
|
ldevUnloadImage(pldev);
|
|
}
|
|
else
|
|
{
|
|
// Return TRUE if the driver is not loaded.
|
|
bReturn = TRUE;
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* EngLoadImage
|
|
*
|
|
* Loads an image that a display of printers driver can then call to execute
|
|
* code
|
|
*
|
|
\**************************************************************************/
|
|
|
|
HANDLE
|
|
APIENTRY
|
|
EngLoadImage(
|
|
LPWSTR pwszDriver
|
|
)
|
|
{
|
|
GDIFunctionID(EngLoadImage);
|
|
|
|
BOOL bLoaded;
|
|
HANDLE h;
|
|
|
|
GreAcquireSemaphoreEx(ghsemDriverMgmt, SEMORDER_DRIVERMGMT, NULL);
|
|
|
|
h = ldevLoadImage(pwszDriver, TRUE, &bLoaded, TRUE);
|
|
|
|
GreReleaseSemaphoreEx(ghsemDriverMgmt);
|
|
|
|
return h;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* EngFindImageProcAddress
|
|
*
|
|
* Returns the address of the specified functions in the module.
|
|
* Special DrvEnableDriver since it is the entry point.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
typedef struct _NEWPROCADDRESS {
|
|
LPSTR lpstrName;
|
|
PVOID pvAddress;
|
|
} NEWPROCADDRESS;
|
|
|
|
// The following define expands 'NEWPROC(x)' to '"x", x':
|
|
|
|
#define NEWPROC(x) #x, x
|
|
|
|
// Table of Eng functions that are new since NT 4.0:
|
|
|
|
NEWPROCADDRESS gaNewProcAddresses[] = {
|
|
NEWPROC(EngQueryPalette),
|
|
NEWPROC(EngSaveFloatingPointState),
|
|
NEWPROC(EngRestoreFloatingPointState),
|
|
NEWPROC(EngSetPointerShape),
|
|
NEWPROC(EngMovePointer),
|
|
NEWPROC(EngSetPointerTag),
|
|
NEWPROC(EngCreateEvent),
|
|
NEWPROC(EngDeleteEvent),
|
|
NEWPROC(EngMapEvent),
|
|
NEWPROC(EngUnmapEvent),
|
|
NEWPROC(EngSetEvent),
|
|
NEWPROC(EngClearEvent),
|
|
NEWPROC(EngReadStateEvent),
|
|
NEWPROC(EngWaitForSingleObject),
|
|
NEWPROC(EngDeleteWnd),
|
|
NEWPROC(EngInitializeSafeSemaphore),
|
|
NEWPROC(EngDeleteSafeSemaphore),
|
|
NEWPROC(HeapVidMemAllocAligned),
|
|
NEWPROC(VidMemFree),
|
|
NEWPROC(EngAlphaBlend),
|
|
NEWPROC(EngGradientFill),
|
|
NEWPROC(EngStretchBltROP),
|
|
NEWPROC(EngPlgBlt),
|
|
NEWPROC(EngTransparentBlt),
|
|
NEWPROC(EngControlSprites),
|
|
NEWPROC(EngLockDirectDrawSurface),
|
|
NEWPROC(EngUnlockDirectDrawSurface),
|
|
NEWPROC(EngMapFile),
|
|
NEWPROC(EngUnmapFile),
|
|
NEWPROC(EngDeleteFile),
|
|
NEWPROC(EngLpkInstalled),
|
|
NEWPROC(BRUSHOBJ_hGetColorTransform),
|
|
NEWPROC(XLATEOBJ_hGetColorTransform),
|
|
NEWPROC(FONTOBJ_pjOpenTypeTablePointer),
|
|
NEWPROC(FONTOBJ_pwszFontFilePaths),
|
|
NEWPROC(FONTOBJ_pfdg),
|
|
NEWPROC(FONTOBJ_pQueryGlyphAttrs),
|
|
NEWPROC(STROBJ_fxCharacterExtra),
|
|
NEWPROC(STROBJ_fxBreakExtra),
|
|
NEWPROC(STROBJ_bGetAdvanceWidths),
|
|
NEWPROC(STROBJ_bEnumPositionsOnly),
|
|
NEWPROC(EngGetPrinterDriver),
|
|
NEWPROC(EngMapFontFileFD),
|
|
NEWPROC(EngUnmapFontFileFD),
|
|
NEWPROC(EngQuerySystemAttribute),
|
|
NEWPROC(HT_Get8BPPMaskPalette),
|
|
#if !defined(_GDIPLUS_)
|
|
NEWPROC(EngGetTickCount),
|
|
NEWPROC(EngFileWrite),
|
|
NEWPROC(EngFileIoControl),
|
|
#endif
|
|
NEWPROC(EngDitherColor),
|
|
NEWPROC(EngModifySurface),
|
|
NEWPROC(EngQueryDeviceAttribute),
|
|
NEWPROC(EngHangNotification),
|
|
NEWPROC(EngNineGrid),
|
|
NEWPROC(EngBugCheckEx),
|
|
};
|
|
|
|
PVOID
|
|
APIENTRY
|
|
EngFindImageProcAddress(
|
|
HANDLE hModule,
|
|
LPSTR lpProcName
|
|
)
|
|
{
|
|
PSYSTEM_GDI_DRIVER_INFORMATION pGdiDriverInfo;
|
|
|
|
PULONG NameTableBase;
|
|
USHORT OrdinalNumber;
|
|
PUSHORT NameOrdinalTableBase;
|
|
ULONG NumberOfNames;
|
|
PULONG AddressTableBase;
|
|
ULONG i;
|
|
|
|
if (!hModule)
|
|
{
|
|
// An 'hModule' of zero means that the driver wants to find
|
|
// the address of a GDI 'Eng' function. Note that this didn't
|
|
// work in the final release of NT 4.0 (this routine would
|
|
// access violate if passed an 'hModule' of zero), so drivers
|
|
// must check GDI's engine version to make sure it's not 4.0,
|
|
// before calling this function.
|
|
//
|
|
// Unfortunately, the Base can't handle loading of 'win32k.sys'
|
|
// at the time of this writing, so we'll just special-case here
|
|
// all the Eng functions that are new since 4.0. This allows
|
|
// drivers to take advantage of NT 4.0 SP3 call-backs when
|
|
// running on SP3, but to still load and run NT 4.0 SP2.
|
|
|
|
for (i=0; i < sizeof(gaNewProcAddresses)/sizeof(NEWPROCADDRESS); i++)
|
|
{
|
|
if (!strcmp(lpProcName, gaNewProcAddresses[i].lpstrName))
|
|
{
|
|
return gaNewProcAddresses[i].pvAddress;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
|
|
} else {
|
|
|
|
pGdiDriverInfo = ((PLDEV)hModule)->pGdiDriverInfo;
|
|
}
|
|
|
|
if (!strncmp(lpProcName,
|
|
"DrvEnableDriver",
|
|
strlen(lpProcName)))
|
|
{
|
|
return (pGdiDriverInfo->EntryPoint);
|
|
}
|
|
|
|
if (pGdiDriverInfo->ExportSectionPointer)
|
|
{
|
|
NameTableBase = (PULONG)((ULONG_PTR)pGdiDriverInfo->ImageAddress +
|
|
(ULONG)pGdiDriverInfo->ExportSectionPointer->AddressOfNames);
|
|
NameOrdinalTableBase = (PUSHORT)((ULONG_PTR)pGdiDriverInfo->ImageAddress +
|
|
(ULONG)pGdiDriverInfo->ExportSectionPointer->AddressOfNameOrdinals);
|
|
|
|
NumberOfNames = pGdiDriverInfo->ExportSectionPointer->NumberOfNames;
|
|
|
|
AddressTableBase = (PULONG)((ULONG_PTR)pGdiDriverInfo->ImageAddress +
|
|
(ULONG_PTR)pGdiDriverInfo->ExportSectionPointer->AddressOfFunctions);
|
|
|
|
for (i=0; i < NumberOfNames; i++)
|
|
{
|
|
if (!strncmp(lpProcName,
|
|
(PCHAR) (NameTableBase[i] + (ULONG_PTR)pGdiDriverInfo->ImageAddress),
|
|
strlen(lpProcName)))
|
|
{
|
|
OrdinalNumber = NameOrdinalTableBase[i];
|
|
|
|
return ((PVOID) ((ULONG_PTR)pGdiDriverInfo->ImageAddress +
|
|
AddressTableBase[OrdinalNumber]));
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* EngUnloadImage
|
|
*
|
|
* Unloads an image loaded using EngLoadImage.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID
|
|
APIENTRY
|
|
EngUnloadImage(
|
|
HANDLE hModule
|
|
)
|
|
{
|
|
ldevUnloadImage((PLDEV)hModule);
|
|
}
|
|
|
|
|
|
/******************************Public*Routine******************************\
|
|
* EngHangNotification
|
|
*
|
|
* This is the engine entry point to notify system that the given device
|
|
* is no longer operable or responsive.
|
|
*
|
|
* hDev must always be a valid device.
|
|
*
|
|
* This call will return EHN_RESTORED if the device has been restored to
|
|
* working order or EHN_ERROR otherwise.
|
|
*
|
|
*
|
|
* History:
|
|
* 12-Sep-2000 -by- Jason Hartman jasonha
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
ULONG
|
|
APIENTRY
|
|
EngHangNotification(
|
|
HDEV hdev,
|
|
PVOID Reserved
|
|
)
|
|
{
|
|
GDIFunctionID(EngHangNotification);
|
|
|
|
ULONG Result = EHN_ERROR;
|
|
PDEVOBJ pdo(hdev);
|
|
|
|
if (pdo.bValid())
|
|
{
|
|
PGRAPHICS_DEVICE PhysDisp = pdo.ppdev->pGraphicsDevice;
|
|
PIO_ERROR_LOG_PACKET perrLogEntry;
|
|
PCWSTR pwszDevice, pwszDesc;
|
|
ULONG cbDevice, cbDesc;
|
|
|
|
if (PhysDisp == (PGRAPHICS_DEVICE) DDML_DRIVER ||
|
|
PhysDisp == NULL)
|
|
{
|
|
RIP("Invalid HDEV.\n");
|
|
return Result;
|
|
}
|
|
|
|
DbgPrint("GDI: EngHangNotification: %ls is not responding.\n", PhysDisp->szWinDeviceName);
|
|
|
|
pwszDevice = PhysDisp->szNtDeviceName;
|
|
cbDevice = (wcslen(pwszDevice)+1)*sizeof(WCHAR);
|
|
pwszDesc = PhysDisp->DeviceDescription;
|
|
cbDesc = (wcslen(pwszDesc)+1)*sizeof(WCHAR);
|
|
|
|
/*
|
|
* Allocate an error packet, fill it out, and write it to the log.
|
|
*/
|
|
perrLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(gpWin32kDriverObject,
|
|
(UCHAR)(cbDevice + cbDesc + FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData)));
|
|
if (perrLogEntry)
|
|
{
|
|
perrLogEntry->ErrorCode = STATUS_INVALID_DEVICE_STATE;
|
|
|
|
perrLogEntry->NumberOfStrings = 2;
|
|
perrLogEntry->StringOffset = FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData);
|
|
RtlCopyMemory(perrLogEntry->DumpData, pwszDevice, cbDevice);
|
|
RtlCopyMemory((PBYTE)perrLogEntry->DumpData + cbDevice, pwszDesc, cbDesc);
|
|
|
|
IoWriteErrorLogEntry(perrLogEntry);
|
|
}
|
|
else
|
|
{
|
|
WARNING("Failed to create error log entry.\n");
|
|
}
|
|
|
|
if (PPFNVALID(pdo,ResetDevice) &&
|
|
(*PPFNDRV(pdo,ResetDevice))(pdo.dhpdev(), NULL) == DRD_SUCCESS)
|
|
{
|
|
Result = EHN_RESTORED;
|
|
}
|
|
else
|
|
{
|
|
RIP("Unable to recover device.\n");
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
/******************************Public*Routine******************************\
|
|
* ldevGetDriverModes
|
|
*
|
|
* Loads the device driver long enough to pass the DrvGetModes call to it.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
ULONG
|
|
ldevGetDriverModes(
|
|
LPWSTR pwszDriver,
|
|
HANDLE hDriver,
|
|
PDEVMODEW *pdm
|
|
)
|
|
{
|
|
PLDEV pldev;
|
|
ULONG ulRet = 0;
|
|
|
|
TRACE_INIT(("ldevGetDriverModes: Entering\n"));
|
|
|
|
*pdm = NULL;
|
|
|
|
//
|
|
// Temporarily locate and load the driver.
|
|
//
|
|
|
|
pldev = ldevLoadDriver(pwszDriver, LDEV_DEVICE_DISPLAY);
|
|
|
|
if (pldev)
|
|
{
|
|
//
|
|
// Locate the function and call it.
|
|
//
|
|
|
|
PFN_DrvGetModes pfn = (PFN_DrvGetModes) pldev->apfn[INDEX_DrvGetModes];
|
|
|
|
if (pfn != NULL)
|
|
{
|
|
ULONG cjSize;
|
|
|
|
if (cjSize = (*pfn)(hDriver, 0, NULL)) { // get modelist size
|
|
|
|
//
|
|
// In NT4 we passed in a buffer of 64K. In NT5 we try to
|
|
// pass in a buffer size based on the number of modes the
|
|
// driver will actually use. However, some drivers are
|
|
// buggy and don't properly return the amount of buffer
|
|
// space they need. So we'll always pass in a buffer
|
|
// at least 64K in length.
|
|
//
|
|
|
|
if (pldev->ulDriverVersion < DDI_DRIVER_VERSION_NT5) {
|
|
cjSize = max(cjSize, 0x10000);
|
|
}
|
|
|
|
if (*pdm = (PDEVMODEW) PALLOCNOZ(cjSize, GDITAG_DRVSUP)) {
|
|
ulRet = (*pfn)(hDriver,cjSize,*pdm);
|
|
} else {
|
|
WARNING("ldevGetDriverModes failed to alloc mem for mode list\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
ldevUnloadImage(pldev);
|
|
}
|
|
|
|
#define MAPDEVMODE_FLAGS (DM_BITSPERPEL | DM_PELSWIDTH | \
|
|
DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS)
|
|
|
|
if (ulRet)
|
|
{
|
|
if (((*pdm)->dmFields & MAPDEVMODE_FLAGS) != MAPDEVMODE_FLAGS)
|
|
{
|
|
ASSERTGDI(FALSE,"DrvGetModes did not set the dmFields value!\n");
|
|
ulRet = 0;
|
|
}
|
|
}
|
|
|
|
TRACE_INIT(("ldevGetDriverModes: Leaving\n"));
|
|
|
|
//
|
|
// Return the driver's result.
|
|
//
|
|
|
|
return(ulRet);
|
|
}
|
|
|
|
#define REGSTR_CCS L"\\Registry\\Machine\\System\\CurrentControlSet"
|
|
#define REGSTR_HP_CCS L"\\Hardware Profiles\\Current\\System\\CurrentControlSet"
|
|
|
|
/**************************************************************************\
|
|
* DrvGetRegistryHandleFromDeviceMap
|
|
*
|
|
* Gets the handle to the registry node for that driver.
|
|
*
|
|
* returns a HANDLE
|
|
*
|
|
*
|
|
* lpMatchString is passed in when the caller wants to make sure the device
|
|
* name (\Device\video0) matches a certain physical device in the registry
|
|
* (\Services\Weitekp9\Device0). We call this routine in a loop with a
|
|
* specific lpMatchString to find that device in the list in DeviceMap.
|
|
*
|
|
* 30-Nov-1992 andreva created
|
|
\**************************************************************************/
|
|
|
|
HANDLE
|
|
DrvGetRegistryHandleFromDeviceMap(
|
|
PGRAPHICS_DEVICE PhysDisp,
|
|
DISP_DRIVER_REGISTRY_TYPE ParamType,
|
|
PULONG pSubId,
|
|
LPWSTR pDriverName,
|
|
PNTSTATUS pStatus,
|
|
USHORT ProtocolType)
|
|
{
|
|
HANDLE hkRegistry = NULL;
|
|
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
NTSTATUS Status;
|
|
HANDLE handle;
|
|
ULONG cbStringSize;
|
|
WCHAR *pdriverRegistryPath = NULL;
|
|
WCHAR *pfullRegistryPath = NULL;
|
|
#ifdef _HYDRA_
|
|
WCHAR *pTerminalServerVideoRegistryPath;
|
|
#endif
|
|
|
|
|
|
TRACE_INIT(("Drv_Trace: GetHandleFromMap: Enter\n"));
|
|
|
|
//
|
|
// Initialize the handle
|
|
//
|
|
|
|
//
|
|
// Start by opening the registry devicemap for video.
|
|
//
|
|
#ifdef _HYDRA_
|
|
if ((pTerminalServerVideoRegistryPath = (WCHAR*)PALLOCMEM(256*sizeof(WCHAR), 'pmtG')) == NULL)
|
|
{
|
|
if (pStatus)
|
|
*pStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
/*
|
|
* Look in TerminalServer VIDEO section for the video driver type as given by client.
|
|
*
|
|
*/
|
|
if (ProtocolType != PROTOCOL_CONSOLE &&
|
|
ProtocolType != PROTOCOL_DISCONNECT) {
|
|
|
|
UnicodeString.Buffer = pTerminalServerVideoRegistryPath;
|
|
UnicodeString.Length = 0;
|
|
UnicodeString.MaximumLength = 255*sizeof(WCHAR);
|
|
|
|
RtlAppendUnicodeToString(&UnicodeString,
|
|
NTAPI_VIDEO_REG_NAME L"\\" );
|
|
RtlAppendUnicodeToString(&UnicodeString,
|
|
G_DisplayDriverNames);
|
|
}
|
|
else
|
|
#endif
|
|
RtlInitUnicodeString(&UnicodeString,
|
|
L"\\Registry\\Machine\\Hardware\\DeviceMap\\Video");
|
|
|
|
TRACE_INIT(("Video Registry path is %ws\n", UnicodeString.Buffer));
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&UnicodeString,
|
|
OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = ZwOpenKey(&handle, KEY_READ, &ObjectAttributes);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
pdriverRegistryPath = (WCHAR*)PALLOCMEM(2*256*sizeof(WCHAR), 'pmtG');
|
|
|
|
if (pdriverRegistryPath == NULL)
|
|
{
|
|
if (pStatus)
|
|
*pStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
pfullRegistryPath = pdriverRegistryPath + 256;
|
|
|
|
if (ProtocolType != PROTOCOL_CONSOLE &&
|
|
ProtocolType != PROTOCOL_DISCONNECT) {
|
|
|
|
//
|
|
//We may have several remote display drivers
|
|
//in the same list, but only one per protocol.
|
|
//They are all registered in the registry as
|
|
//"\Device\Video0"
|
|
//
|
|
RtlInitUnicodeString(&UnicodeString,
|
|
L"\\Device\\Video0");
|
|
|
|
} else {
|
|
|
|
RtlInitUnicodeString(&UnicodeString,
|
|
PhysDisp->szNtDeviceName);
|
|
}
|
|
|
|
//
|
|
// Get the name of the driver based on the device name.
|
|
//
|
|
|
|
Status = ZwQueryValueKey(handle,
|
|
&UnicodeString,
|
|
KeyValueFullInformation,
|
|
pdriverRegistryPath,
|
|
512,
|
|
&cbStringSize);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// Look up in the registry for the kernel driver node (it
|
|
// is a full path to the driver node) so we can get the
|
|
// display driver info.
|
|
//
|
|
|
|
LPWSTR lpstrStart;
|
|
LPWSTR lpstrDriverRegistryPath;
|
|
LPWSTR lpstrEndPath;
|
|
UNICODE_STRING FullRegistryPath;
|
|
|
|
|
|
//
|
|
// We can use wcsstr since we are guaranteed to find "Control" or
|
|
// "Services" in the string, and we won't run off the end of the
|
|
// string. Capitalize it so we don't have problems with different
|
|
// types of paths.
|
|
//
|
|
|
|
lpstrStart = (LPWSTR)((PUCHAR)pdriverRegistryPath +
|
|
((PKEY_VALUE_FULL_INFORMATION)pdriverRegistryPath)->DataOffset);
|
|
|
|
// We only want RegKey here
|
|
if (ParamType == DispDriverRegKey)
|
|
{
|
|
wcsncpy(pDriverName, lpstrStart, 127);
|
|
ZwCloseKey(handle);
|
|
if (pStatus)
|
|
*pStatus = Status;
|
|
goto Exit;
|
|
}
|
|
|
|
while (*lpstrStart)
|
|
{
|
|
*lpstrStart = (USHORT)(toupper(*lpstrStart));
|
|
lpstrStart++;
|
|
}
|
|
|
|
lpstrDriverRegistryPath = wcsstr((LPWSTR)((PUCHAR)pdriverRegistryPath +
|
|
((PKEY_VALUE_FULL_INFORMATION)pdriverRegistryPath)->DataOffset),
|
|
L"\\CONTROL\\");
|
|
|
|
if (lpstrDriverRegistryPath == NULL) {
|
|
|
|
lpstrDriverRegistryPath = wcsstr((LPWSTR)((PUCHAR)pdriverRegistryPath +
|
|
((PKEY_VALUE_FULL_INFORMATION)pdriverRegistryPath)->DataOffset),
|
|
L"\\SERVICES");
|
|
}
|
|
|
|
//
|
|
// Save the service name as the description in case it's needed
|
|
// later
|
|
//
|
|
|
|
if (pDriverName)
|
|
{
|
|
LPWSTR pName = pDriverName;
|
|
ULONG i = 31;
|
|
HANDLE CommonSubkeyHandle;
|
|
LPWSTR CommonSubkeyBuffer = NULL;
|
|
LPWSTR pSubkeyBuffer = NULL;
|
|
LONG BufferLen = 0, RegistryPathLen = 0;
|
|
|
|
//
|
|
// Get the registry path and find the last '\'
|
|
//
|
|
|
|
RegistryPathLen = wcslen((LPWSTR)((PUCHAR)pdriverRegistryPath +
|
|
((PKEY_VALUE_FULL_INFORMATION)pdriverRegistryPath)->DataOffset));
|
|
BufferLen = (max(RegistryPathLen + 6, 32) * sizeof(WCHAR));
|
|
|
|
CommonSubkeyBuffer = (LPWSTR) PALLOCMEM(BufferLen, GDITAG_DRVSUP);
|
|
|
|
if (CommonSubkeyBuffer)
|
|
{
|
|
RtlZeroMemory(CommonSubkeyBuffer, BufferLen);
|
|
|
|
wcscpy(CommonSubkeyBuffer, (LPWSTR)((PUCHAR)pdriverRegistryPath +
|
|
((PKEY_VALUE_FULL_INFORMATION)pdriverRegistryPath)->DataOffset));
|
|
|
|
pSubkeyBuffer = CommonSubkeyBuffer + RegistryPathLen - 1;
|
|
|
|
while ((pSubkeyBuffer > CommonSubkeyBuffer) &&
|
|
(*pSubkeyBuffer != L'\\'))
|
|
{
|
|
pSubkeyBuffer--;
|
|
}
|
|
|
|
if (*pSubkeyBuffer == L'\\')
|
|
{
|
|
pSubkeyBuffer++;
|
|
|
|
//
|
|
// Open the Video subkey
|
|
//
|
|
|
|
wcscpy(pSubkeyBuffer, L"Video");
|
|
|
|
RtlInitUnicodeString(&UnicodeString, CommonSubkeyBuffer);
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&UnicodeString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = ZwOpenKey(&CommonSubkeyHandle,
|
|
KEY_READ,
|
|
&ObjectAttributes);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
ULONG cbLegacyRegistryPathSize;
|
|
|
|
//
|
|
// Get the service name
|
|
//
|
|
|
|
RtlInitUnicodeString(&UnicodeString, L"Service");
|
|
|
|
RtlZeroMemory(CommonSubkeyBuffer, BufferLen);
|
|
|
|
Status = ZwQueryValueKey(CommonSubkeyHandle,
|
|
&UnicodeString,
|
|
KeyValueFullInformation,
|
|
CommonSubkeyBuffer,
|
|
BufferLen,
|
|
&cbLegacyRegistryPathSize);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// Convert to upper chars ...
|
|
//
|
|
|
|
pSubkeyBuffer = CommonSubkeyBuffer;
|
|
|
|
while (*pSubkeyBuffer)
|
|
{
|
|
*pSubkeyBuffer = (USHORT)(toupper(*pSubkeyBuffer));
|
|
pSubkeyBuffer++;
|
|
}
|
|
|
|
//
|
|
// Copy the service name
|
|
//
|
|
|
|
pSubkeyBuffer = CommonSubkeyBuffer;
|
|
|
|
while ((i--) && (*pSubkeyBuffer != NULL))
|
|
{
|
|
*pName++ = *pSubkeyBuffer++;
|
|
|
|
if ((i == 28) && (_wcsnicmp(pDriverName, L"VGA", 3) == 0))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ZwCloseKey(CommonSubkeyHandle);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This should not happen ...
|
|
//
|
|
|
|
ASSERTGDI(FALSE, "invalid registry key\n");
|
|
}
|
|
|
|
VFREEMEM(CommonSubkeyBuffer);
|
|
}
|
|
|
|
*pName = UNICODE_NULL;
|
|
|
|
//
|
|
// Make sure we do not set a remote device or the disconnect device as the VGA device.
|
|
//
|
|
|
|
if (!(PhysDisp->stateFlags &(DISPLAY_DEVICE_REMOTE | DISPLAY_DEVICE_DISCONNECT) ) ) {
|
|
gPhysDispVGA = PhysDisp;
|
|
}
|
|
}
|
|
|
|
//
|
|
// This sectione is for per Monitor settings. Each monitor has UID.
|
|
// So we save under Servive\XXXX\DeviceX\UID
|
|
//
|
|
|
|
if (pSubId)
|
|
{
|
|
swprintf(lpstrDriverRegistryPath+wcslen(lpstrDriverRegistryPath),
|
|
L"\\Mon%08X",
|
|
*pSubId);
|
|
}
|
|
|
|
//
|
|
// Start composing the fully qualified path name.
|
|
//
|
|
|
|
FullRegistryPath.Buffer = pfullRegistryPath;
|
|
FullRegistryPath.Length = 0;
|
|
FullRegistryPath.MaximumLength = 255*sizeof(WCHAR);
|
|
|
|
RtlAppendUnicodeToString(&FullRegistryPath, REGSTR_CCS);
|
|
|
|
//
|
|
// If we want the hardware profile, insert the hardware profile
|
|
// in there
|
|
//
|
|
|
|
if ((ParamType == DispDriverRegHardwareProfile) ||
|
|
(ParamType == DispDriverRegHardwareProfileCreate))
|
|
{
|
|
TRACE_INIT(("Drv_Trace: GetHandleFromMap: using a hardware profile\n"));
|
|
RtlAppendUnicodeToString(&FullRegistryPath, REGSTR_HP_CCS);
|
|
}
|
|
|
|
//
|
|
// If we have the create Options, we have to create the subkeys
|
|
// otherwise, just open the key
|
|
//
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&FullRegistryPath,
|
|
OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL);
|
|
|
|
//
|
|
// Check if the subkeys need to be created.
|
|
//
|
|
|
|
if (ParamType == DispDriverRegHardwareProfileCreate)
|
|
{
|
|
TRACE_INIT(("Drv_Trace: GetHandleFromMap: creating a hardware profile\n"));
|
|
|
|
//
|
|
// We are guaranteed to go through the loop at least once,
|
|
// which will ensure the status is set properly.
|
|
//
|
|
// Basically, find the '\' replace it by NULL and add that
|
|
// partial string to the full path (so we can create that
|
|
// subkey), put back the '\' and keep on going for the next
|
|
// string. We must also add the end of the string.
|
|
//
|
|
|
|
do
|
|
{
|
|
lpstrEndPath = wcschr(lpstrDriverRegistryPath + 1, L'\\');
|
|
|
|
if (lpstrEndPath != NULL)
|
|
{
|
|
*lpstrEndPath = UNICODE_NULL;
|
|
}
|
|
|
|
RtlAppendUnicodeToString(&FullRegistryPath,
|
|
lpstrDriverRegistryPath);
|
|
|
|
//
|
|
// Close the previous key if necessary.
|
|
//
|
|
|
|
if (hkRegistry)
|
|
{
|
|
ZwCloseKey(hkRegistry);
|
|
}
|
|
|
|
//
|
|
// Create the Key.
|
|
//
|
|
|
|
Status = ZwCreateKey(&hkRegistry,
|
|
(ACCESS_MASK) NULL,
|
|
&ObjectAttributes,
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
hkRegistry = NULL;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check to see if we need to loop again.
|
|
//
|
|
|
|
if (lpstrEndPath == NULL)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
*lpstrEndPath = L'\\';
|
|
lpstrDriverRegistryPath = lpstrEndPath;
|
|
}
|
|
|
|
|
|
} while(1);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE_INIT(("Drv_Trace: GetHandleFromMap: failed to create key\n"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RtlAppendUnicodeToString(&FullRegistryPath,
|
|
lpstrDriverRegistryPath);
|
|
|
|
Status = ZwOpenKey(&hkRegistry, KEY_READ, &ObjectAttributes);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE_INIT(("Drv_Trace: GetHandleFromMap: failed to open key\n"));
|
|
|
|
//
|
|
// We set this special status so the looping code in the
|
|
// video port can handle unconfigured devices properly
|
|
// (in the case where the second video card entry may not
|
|
// be present).
|
|
//
|
|
|
|
Status = STATUS_DEVICE_CONFIGURATION_ERROR;
|
|
}
|
|
}
|
|
|
|
TRACE_INIT(("Drv_Trace: GetHandleFromMap: reg-key path =\n\t%ws\n",
|
|
pfullRegistryPath));
|
|
}
|
|
|
|
|
|
ZwCloseKey(handle);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE_INIT(("Drv_Trace: GetHandleFromMap: Error opening registry - status = %08lx\n",
|
|
Status));
|
|
}
|
|
|
|
if (pStatus)
|
|
{
|
|
*pStatus = Status;
|
|
}
|
|
|
|
TRACE_INIT(("Drv_Trace: GetHandleFromMap: Exit\n\n"));
|
|
|
|
Exit:
|
|
|
|
#ifdef _HYDRA_
|
|
if (pTerminalServerVideoRegistryPath)
|
|
VFREEMEM(pTerminalServerVideoRegistryPath);
|
|
#endif
|
|
|
|
if (pdriverRegistryPath)
|
|
VFREEMEM(pdriverRegistryPath);
|
|
|
|
return hkRegistry;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* DrvPrepareForEARecovery
|
|
*
|
|
* Remove all devices other than the VGA device from the graphics
|
|
* device list.
|
|
*
|
|
* In multi-mon we could just disable the now "dead" display. In single
|
|
* mon we need to disable the "high-res" display and switch to VGA.
|
|
*
|
|
* 27-Mar-2002 ericks created
|
|
\**************************************************************************/
|
|
|
|
VOID
|
|
DrvPrepareForEARecovery(
|
|
VOID
|
|
)
|
|
|
|
{
|
|
ULONG ulBytes;
|
|
|
|
if (gPhysDispVGA) {
|
|
|
|
GreDeviceIoControl(gPhysDispVGA->pDeviceHandle,
|
|
IOCTL_VIDEO_PREPARE_FOR_EARECOVERY,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
&ulBytes);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* DrvGetDisplayDriverNames
|
|
*
|
|
* Get the display driver name out of the registry.
|
|
*
|
|
* 12-Jan-1994 andreva created
|
|
\**************************************************************************/
|
|
|
|
typedef struct _DRV_NAMES {
|
|
ULONG cNames;
|
|
struct {
|
|
HANDLE hDriver;
|
|
LPWSTR lpDisplayName;
|
|
} D[1];
|
|
} DRV_NAMES, *PDRV_NAMES;
|
|
|
|
PDRV_NAMES
|
|
DrvGetDisplayDriverNames(
|
|
PGRAPHICS_DEVICE PhysDisp
|
|
)
|
|
{
|
|
DWORD status;
|
|
LPWSTR lptmpdisplay;
|
|
DWORD cCountNames;
|
|
DWORD cCountNamesDevice;
|
|
PDRV_NAMES lpNames = NULL;
|
|
ULONG cb = 0;
|
|
ULONG cbVga = 0;
|
|
|
|
//
|
|
// At this point, create the array, and put in the VGA driver
|
|
// if necessary. (We morphed this from the ModeX hack.)
|
|
//
|
|
|
|
if (PhysDisp->DisplayDriverNames)
|
|
{
|
|
//
|
|
// Count the names in the list (first for PhysDisp)
|
|
//
|
|
|
|
cCountNames = 0;
|
|
lptmpdisplay = PhysDisp->DisplayDriverNames;
|
|
|
|
while (*lptmpdisplay != UNICODE_NULL)
|
|
{
|
|
cCountNames++;
|
|
|
|
while (*lptmpdisplay != UNICODE_NULL)
|
|
{
|
|
lptmpdisplay++;
|
|
cb += 2;
|
|
}
|
|
|
|
lptmpdisplay++;
|
|
cb += 2;
|
|
}
|
|
|
|
//
|
|
// Track the number of display driver names for the root device
|
|
//
|
|
|
|
cCountNamesDevice = cCountNames;
|
|
|
|
//
|
|
// Now if this PhysDisp is associated with the VGA, then add
|
|
// the VGA display drivers as well.
|
|
//
|
|
|
|
if (PhysDisp->pVgaDevice &&
|
|
gFullscreenGraphicsDevice.pDeviceHandle &&
|
|
PhysDisp->pVgaDevice->DisplayDriverNames) {
|
|
|
|
lptmpdisplay = PhysDisp->pVgaDevice->DisplayDriverNames;
|
|
|
|
while (*lptmpdisplay != UNICODE_NULL)
|
|
{
|
|
cCountNames++;
|
|
|
|
while (*lptmpdisplay != UNICODE_NULL)
|
|
{
|
|
lptmpdisplay++;
|
|
cbVga += 2;
|
|
}
|
|
|
|
lptmpdisplay++;
|
|
cbVga += 2;
|
|
}
|
|
cbVga += 2;
|
|
}
|
|
|
|
//
|
|
// Alocate the names structure.
|
|
//
|
|
|
|
if (lpNames = (PDRV_NAMES) PALLOCNOZ(cb + cbVga + 2 + sizeof(DRV_NAMES) *
|
|
(cCountNames + 1),
|
|
GDITAG_DRVSUP))
|
|
{
|
|
lptmpdisplay = (LPWSTR) (((PUCHAR)lpNames) +
|
|
sizeof(DRV_NAMES) * (cCountNames + 1));
|
|
|
|
RtlCopyMemory(lptmpdisplay,
|
|
PhysDisp->DisplayDriverNames,
|
|
cb + 2);
|
|
|
|
if (cbVga) {
|
|
|
|
RtlCopyMemory((LPWSTR)((ULONG_PTR)lptmpdisplay + cb),
|
|
PhysDisp->pVgaDevice->DisplayDriverNames,
|
|
cbVga + 2);
|
|
}
|
|
|
|
//
|
|
// Set all the names pointers in the names structure
|
|
//
|
|
|
|
lpNames->cNames = 0;
|
|
|
|
while (*lptmpdisplay != UNICODE_NULL)
|
|
{
|
|
lpNames->D[lpNames->cNames].lpDisplayName = lptmpdisplay;
|
|
|
|
if (lpNames->cNames < cCountNamesDevice) {
|
|
lpNames->D[lpNames->cNames].hDriver = PhysDisp->pDeviceHandle;
|
|
} else {
|
|
lpNames->D[lpNames->cNames].hDriver = PhysDisp->pVgaDevice->pDeviceHandle;
|
|
|
|
//
|
|
// For now, only return the first in the list of vga
|
|
// display driver names (vga.dll)
|
|
//
|
|
|
|
lpNames->cNames++;
|
|
break;
|
|
}
|
|
lpNames->cNames++;
|
|
|
|
while (*lptmpdisplay != UNICODE_NULL)
|
|
{
|
|
lptmpdisplay++;
|
|
}
|
|
lptmpdisplay++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return lpNames;
|
|
}
|
|
|
|
//
|
|
// We need to update monitor PDOs whenever reenumaration of devices has been occured
|
|
// This function has to be called every time before PhysDisp->MonitorDevices is to be used
|
|
//
|
|
VOID UpdateMonitorDevices(VOID)
|
|
{
|
|
PGRAPHICS_DEVICE PhysDisp;
|
|
ULONG numMonitorDevice, i;
|
|
|
|
for (PhysDisp = gpGraphicsDeviceList;
|
|
PhysDisp != NULL;
|
|
PhysDisp = PhysDisp->pNextGraphicsDevice)
|
|
{
|
|
PVIDEO_MONITOR_DEVICE pMonitorDevices = NULL;
|
|
BOOL bNoMonitor = TRUE;
|
|
|
|
if ((PhysDisp->pDeviceHandle != NULL ) &&
|
|
NT_SUCCESS(GreDeviceIoControl(PhysDisp->pDeviceHandle,
|
|
IOCTL_VIDEO_ENUM_MONITOR_PDO,
|
|
NULL,
|
|
0,
|
|
&pMonitorDevices,
|
|
sizeof(PVOID),
|
|
&numMonitorDevice))
|
|
)
|
|
{
|
|
if (pMonitorDevices != NULL)
|
|
{
|
|
numMonitorDevice = 0;
|
|
while (pMonitorDevices[numMonitorDevice].pdo != NULL)
|
|
{
|
|
ObDereferenceObject(pMonitorDevices[numMonitorDevice].pdo);
|
|
numMonitorDevice++;
|
|
}
|
|
|
|
//
|
|
// The buffer always grows
|
|
//
|
|
if (PhysDisp->numMonitorDevice < numMonitorDevice)
|
|
{
|
|
if (PhysDisp->MonitorDevices)
|
|
{
|
|
VFREEMEM(PhysDisp->MonitorDevices);
|
|
}
|
|
PhysDisp->MonitorDevices = (PVIDEO_MONITOR_DEVICE)PALLOCMEM(sizeof(VIDEO_MONITOR_DEVICE) * numMonitorDevice,
|
|
GDITAG_GDEVICE);
|
|
//
|
|
// If fail, do cleanup here
|
|
//
|
|
if (PhysDisp->MonitorDevices == NULL)
|
|
{
|
|
PhysDisp->numMonitorDevice = 0;
|
|
|
|
ExFreePool(pMonitorDevices);
|
|
return;
|
|
}
|
|
}
|
|
|
|
PhysDisp->numMonitorDevice = numMonitorDevice;
|
|
|
|
for (i = 0; i < numMonitorDevice; i++)
|
|
{
|
|
PhysDisp->MonitorDevices[i].flag = 0;
|
|
if (pMonitorDevices[i].flag & VIDEO_CHILD_ACTIVE) {
|
|
PhysDisp->MonitorDevices[i].flag |= DISPLAY_DEVICE_ACTIVE;
|
|
}
|
|
if ((pMonitorDevices[i].flag & VIDEO_CHILD_DETACHED) == 0) {
|
|
PhysDisp->MonitorDevices[i].flag |= DISPLAY_DEVICE_ATTACHED;
|
|
}
|
|
if ((pMonitorDevices[i].flag & VIDEO_CHILD_NOPRUNE_FREQ) == 0) {
|
|
PhysDisp->MonitorDevices[i].flag |= DISPLAY_DEVICE_PRUNE_FREQ;
|
|
}
|
|
if ((pMonitorDevices[i].flag & VIDEO_CHILD_NOPRUNE_RESOLUTION) == 0) {
|
|
PhysDisp->MonitorDevices[i].flag |= DISPLAY_DEVICE_PRUNE_RESOLUTION;
|
|
}
|
|
PhysDisp->MonitorDevices[i].pdo = pMonitorDevices[i].pdo;
|
|
PhysDisp->MonitorDevices[i].HwID = pMonitorDevices[i].HwID;
|
|
|
|
bNoMonitor = FALSE;
|
|
}
|
|
|
|
ExFreePool(pMonitorDevices);
|
|
}
|
|
}
|
|
|
|
if (bNoMonitor)
|
|
{
|
|
if (PhysDisp->MonitorDevices)
|
|
{
|
|
VFREEMEM(PhysDisp->MonitorDevices);
|
|
}
|
|
PhysDisp->numMonitorDevice = 0;
|
|
PhysDisp->MonitorDevices = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
dbgDumpDevmode(
|
|
PDEVMODEW pdm
|
|
)
|
|
{
|
|
TRACE_INIT((" Size = %d\n", pdm->dmSize));
|
|
TRACE_INIT((" Fields = %08lx\n", pdm->dmFields));
|
|
TRACE_INIT((" XPosition = %d\n", pdm->dmPosition.x));
|
|
TRACE_INIT((" YPosition = %d\n", pdm->dmPosition.y));
|
|
TRACE_INIT((" Orientation = %d\n", pdm->dmDisplayOrientation));
|
|
TRACE_INIT((" FixedOutput = %d\n", pdm->dmDisplayFixedOutput));
|
|
TRACE_INIT((" XResolution = %d\n", pdm->dmPelsWidth));
|
|
TRACE_INIT((" YResolution = %d\n", pdm->dmPelsHeight));
|
|
TRACE_INIT((" Bpp = %d\n", pdm->dmBitsPerPel));
|
|
TRACE_INIT((" Frequency = %d\n", pdm->dmDisplayFrequency));
|
|
TRACE_INIT((" Flags = %d\n", pdm->dmDisplayFlags));
|
|
TRACE_INIT((" XPanning = %d\n", pdm->dmPanningWidth));
|
|
TRACE_INIT((" YPanning = %d\n", pdm->dmPanningHeight));
|
|
TRACE_INIT((" DPI = %d\n", pdm->dmLogPixels));
|
|
TRACE_INIT((" DriverExtra = %d", pdm->dmDriverExtra));
|
|
if (pdm->dmDriverExtra)
|
|
{
|
|
TRACE_INIT((" - %08lx %08lx\n",
|
|
*(PULONG)(((PUCHAR)pdm)+pdm->dmSize),
|
|
*(PULONG)(((PUCHAR)pdm)+pdm->dmSize + 4)));
|
|
}
|
|
else
|
|
{
|
|
TRACE_INIT(("\n"));
|
|
}
|
|
}
|
|
|
|
#define NUM_DISPLAY_PARAMETERS 13
|
|
#define NUM_DISPLAY_DRIVER_EXTRA (NUM_DISPLAY_PARAMETERS - 1)
|
|
|
|
static
|
|
LPWSTR DefaultSettings[NUM_DISPLAY_PARAMETERS] = {
|
|
L"DefaultSettings.BitsPerPel",
|
|
L"DefaultSettings.XResolution",
|
|
L"DefaultSettings.YResolution",
|
|
L"DefaultSettings.VRefresh",
|
|
L"DefaultSettings.Flags",
|
|
L"DefaultSettings.XPanning",
|
|
L"DefaultSettings.YPanning",
|
|
L"DefaultSettings.Orientation",
|
|
L"DefaultSettings.FixedOutput",
|
|
L"Attach.RelativeX",
|
|
L"Attach.RelativeY",
|
|
L"Attach.ToDesktop",
|
|
L"DefaultSettings.DriverExtra" // MUST be at the end
|
|
};
|
|
|
|
static
|
|
LPWSTR AttachedSettings[] = {
|
|
L"Attach.PrimaryDevice",
|
|
L"Attach.ToDesktop",
|
|
};
|
|
|
|
static
|
|
LPWSTR SoftwareSettings[] = {
|
|
L"DriverDesc",
|
|
L"InstalledDisplayDrivers",
|
|
L"MultiDisplayDriver",
|
|
L"MirrorDriver",
|
|
L"VgaCompatible",
|
|
L"Device Description",
|
|
L"HardwareInformation.AdapterString",
|
|
L"HardwareInformation.ChipType",
|
|
};
|
|
|
|
NTSTATUS
|
|
DrvDriverExtraCallback(
|
|
PWSTR ValueName,
|
|
ULONG ValueType,
|
|
PVOID ValueData,
|
|
ULONG ValueLength,
|
|
PVOID Context,
|
|
PVOID EntryContext)
|
|
|
|
{
|
|
|
|
PDEVMODEW pdevmode = (PDEVMODEW) EntryContext;
|
|
|
|
//
|
|
// Put the driver extra data in the right place, if necessary.
|
|
//
|
|
|
|
pdevmode->dmDriverExtra = min(pdevmode->dmDriverExtra, (USHORT)ValueLength);
|
|
|
|
RtlMoveMemory(pdevmode+1,
|
|
ValueData,
|
|
pdevmode->dmDriverExtra);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
UNREFERENCED_PARAMETER(Context);
|
|
UNREFERENCED_PARAMETER(ValueType);
|
|
UNREFERENCED_PARAMETER(ValueName);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* DrvGetDisplayDriverParameters
|
|
*
|
|
* Reads the resolution parameters from the registry.
|
|
*
|
|
* NOTE:
|
|
* We assume the caller has initialized the DEVMODE to zero,
|
|
* and that the DEVMODE is the current size for the system.
|
|
* We only look at the dmDriverExtra field to determine any extra size.
|
|
* We do check the dmSize for debugging purposes.
|
|
*
|
|
* CRIT not needed
|
|
*
|
|
* 25-Jan-1995 andreva created
|
|
\**************************************************************************/
|
|
|
|
NTSTATUS
|
|
DrvGetDisplayDriverParameters(
|
|
PGRAPHICS_DEVICE PhysDisp,
|
|
PDEVMODEW pdevmode,
|
|
BOOL bEmptyDevmode,
|
|
BOOL bFromMonitor
|
|
)
|
|
{
|
|
ULONG i, k;
|
|
NTSTATUS retStatus;
|
|
HANDLE hkRegistry;
|
|
ULONG attached = 0;
|
|
|
|
DISP_DRIVER_REGISTRY_TYPE registryParam = DispDriverRegHardwareProfile;
|
|
DWORD nullValue = 0;
|
|
|
|
//
|
|
// Our current algorithm is to save or get things from the hardware profile
|
|
// first, and then try the global profile as a backup.
|
|
//
|
|
// NOTE ??? For saving, should we always back propagate the changes to the
|
|
// global settings also ? We do this at this point.
|
|
//
|
|
|
|
RTL_QUERY_REGISTRY_TABLE QueryTable[] = {
|
|
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT, NULL, &pdevmode->dmBitsPerPel,
|
|
REG_NONE, NULL, 0},
|
|
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT, NULL, &pdevmode->dmPelsWidth,
|
|
REG_NONE, NULL, 0},
|
|
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT, NULL, &pdevmode->dmPelsHeight,
|
|
REG_NONE, NULL, 0},
|
|
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT, NULL, &pdevmode->dmDisplayFrequency,
|
|
REG_NONE, NULL, 0},
|
|
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT, NULL, &pdevmode->dmDisplayFlags,
|
|
REG_NONE, NULL, 0},
|
|
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT, NULL, &pdevmode->dmPanningWidth,
|
|
REG_NONE, NULL, 0},
|
|
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT, NULL, &pdevmode->dmPanningHeight,
|
|
REG_NONE, NULL, 0},
|
|
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT, NULL, &pdevmode->dmDisplayOrientation,
|
|
REG_NONE, NULL, 0},
|
|
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT, NULL, &pdevmode->dmDisplayFixedOutput,
|
|
REG_NONE, NULL, 0},
|
|
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT, NULL, &pdevmode->dmPosition.x,
|
|
REG_NONE, NULL, 0},
|
|
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT, NULL, &pdevmode->dmPosition.y,
|
|
REG_NONE, NULL, 0},
|
|
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT, NULL, &attached,
|
|
REG_NONE, NULL, 0},
|
|
|
|
// if the value is not there, we want the call to succeed anyway.
|
|
// so specify a vlue that is NULL modulo 64K !
|
|
|
|
{DrvDriverExtraCallback, 0, NULL, pdevmode,
|
|
REG_DWORD, &nullValue, 0x10000},
|
|
|
|
{NULL, 0, NULL}
|
|
};
|
|
|
|
TRACE_INIT(("Drv_Trace: GetDriverParams\n"));
|
|
|
|
//
|
|
// Special debug code to ensure that anyone who calls this API
|
|
// knows what they are doing, and we don't end up in here with a
|
|
// "random" devmode that does not ensure sizes.
|
|
//
|
|
|
|
ASSERTGDI(pdevmode->dmSize == 0xDDDD, "DEVMODE not set to DDDD\n");
|
|
|
|
//
|
|
// If there is no place for the Driver Extra data, don't ask for it.
|
|
// This will just cause the code not to read that value
|
|
//
|
|
|
|
if (pdevmode->dmDriverExtra == 0)
|
|
{
|
|
QueryTable[NUM_DISPLAY_PARAMETERS - 1].Flags = 0;
|
|
pdevmode->dmDriverExtra = 0;
|
|
}
|
|
|
|
//
|
|
// We assume that the DEVMODE was previously zeroed out by the caller
|
|
//
|
|
|
|
retStatus = STATUS_SUCCESS;
|
|
|
|
if (bEmptyDevmode)
|
|
{
|
|
//
|
|
// We want an empty DEVMODE (except for the LogPixels).
|
|
//
|
|
|
|
TRACE_INIT(("Drv_Trace: GetDriverParams: Default (empty) DEVMODE\n"));
|
|
|
|
RtlZeroMemory(pdevmode, sizeof(DEVMODEW));
|
|
}
|
|
else
|
|
{
|
|
|
|
#if LATER
|
|
//
|
|
// Let's try to get the per-user settings first.
|
|
//
|
|
|
|
TRACE_INIT(("Drv_Trace: GetDriverParams: USER Settings\n"));
|
|
|
|
for (i=0; i < NUM_DISPLAY_PARAMETERS; i++)
|
|
{
|
|
QueryTable[i].Name = DefaultSettings[i];
|
|
}
|
|
|
|
retStatus = RtlQueryRegistryValues(RTL_REGISTRY_USER,
|
|
NULL,
|
|
&QueryTable[0],
|
|
NULL,
|
|
NULL);
|
|
if (!NT_SUCCESS(retStatus))
|
|
|
|
#endif
|
|
|
|
TRACE_INIT(("Drv_Trace: GetDriverParams: Hardware Profile Settings\n"));
|
|
|
|
for (i = 0; i < NUM_DISPLAY_PARAMETERS; i++)
|
|
QueryTable[i].Name = (PWSTR)DefaultSettings[i];
|
|
|
|
if (bFromMonitor)
|
|
{
|
|
//
|
|
// We retrieve from per-monitor settings first, only if failed then we go to old registry
|
|
// And if we have multiple active monitors, we pick the smaller mode
|
|
//
|
|
|
|
UpdateMonitorDevices();
|
|
|
|
DEVMODEW tmpDevMode, pickedDevMode;
|
|
|
|
// Save original pdevmode away. We are using pdevmode to retrieve registry
|
|
RtlCopyMemory(&tmpDevMode, pdevmode, sizeof(DEVMODEW));
|
|
pdevmode->dmBitsPerPel = 0;
|
|
pdevmode->dmPelsWidth = 0;
|
|
pdevmode->dmPelsHeight = 0;
|
|
pdevmode->dmDisplayFrequency = 0;
|
|
pdevmode->dmDisplayFlags = 0;
|
|
pdevmode->dmPanningWidth = 0;
|
|
pdevmode->dmPanningHeight = 0;
|
|
pdevmode->dmPosition.x = 0;
|
|
pdevmode->dmPosition.y = 0;
|
|
pdevmode->dmDisplayOrientation = 0;
|
|
pdevmode->dmDisplayFixedOutput = 0;
|
|
RtlZeroMemory(&pickedDevMode, sizeof(DEVMODEW));
|
|
|
|
k = PhysDisp->numMonitorDevice;
|
|
for (i = 0; i < PhysDisp->numMonitorDevice; i++)
|
|
{
|
|
if (IS_ATTACHED_ACTIVE(PhysDisp->MonitorDevices[i].flag))
|
|
{
|
|
hkRegistry = DrvGetRegistryHandleFromDeviceMap(PhysDisp,
|
|
registryParam,
|
|
&PhysDisp->MonitorDevices[i].HwID,
|
|
NULL,
|
|
NULL,
|
|
gProtocolType);
|
|
if (hkRegistry)
|
|
{
|
|
retStatus = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
|
|
(PWSTR)hkRegistry,
|
|
&QueryTable[0],
|
|
NULL,
|
|
NULL);
|
|
ZwCloseKey(hkRegistry);
|
|
}
|
|
else
|
|
continue;
|
|
|
|
if (!NT_SUCCESS(retStatus))
|
|
continue;
|
|
|
|
if ((pdevmode->dmBitsPerPel == 0) ||
|
|
(pdevmode->dmPelsWidth == 0) ||
|
|
(pdevmode->dmPelsHeight == 0)
|
|
)
|
|
continue;
|
|
|
|
//
|
|
// Pick the smallest mode
|
|
//
|
|
if (pickedDevMode.dmPelsWidth == 0 ||
|
|
pdevmode->dmPelsWidth < pickedDevMode.dmPelsWidth ||
|
|
(pdevmode->dmPelsWidth == pickedDevMode.dmPelsWidth &&
|
|
pdevmode->dmPelsHeight < pickedDevMode.dmPelsHeight)
|
|
)
|
|
{
|
|
k = i;
|
|
pickedDevMode.dmPelsWidth = pdevmode->dmPelsWidth;
|
|
pickedDevMode.dmPelsHeight = pdevmode->dmPelsHeight;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Restore original pdevmode
|
|
pdevmode->dmDriverExtra = tmpDevMode.dmDriverExtra;
|
|
pdevmode->dmBitsPerPel = tmpDevMode.dmBitsPerPel;
|
|
pdevmode->dmPelsWidth = tmpDevMode.dmPelsWidth;
|
|
pdevmode->dmPelsHeight = tmpDevMode.dmPelsHeight;
|
|
pdevmode->dmDisplayFrequency = tmpDevMode.dmDisplayFrequency;
|
|
pdevmode->dmDisplayFlags = tmpDevMode.dmDisplayFlags;
|
|
pdevmode->dmPanningWidth = tmpDevMode.dmPanningWidth;
|
|
pdevmode->dmPanningHeight = tmpDevMode.dmPanningHeight;
|
|
pdevmode->dmPosition.x = tmpDevMode.dmPosition.x;
|
|
pdevmode->dmPosition.y = tmpDevMode.dmPosition.y;
|
|
pdevmode->dmDisplayOrientation = tmpDevMode.dmDisplayOrientation;
|
|
pdevmode->dmDisplayFixedOutput = tmpDevMode.dmDisplayFixedOutput;
|
|
}
|
|
|
|
if (k < PhysDisp->numMonitorDevice && bFromMonitor)
|
|
{
|
|
//
|
|
// Come here if we succeed to get per monitor settings
|
|
//
|
|
retStatus = STATUS_UNSUCCESSFUL;
|
|
|
|
hkRegistry = DrvGetRegistryHandleFromDeviceMap(PhysDisp,
|
|
registryParam,
|
|
&PhysDisp->MonitorDevices[k].HwID,
|
|
NULL,
|
|
NULL,
|
|
gProtocolType );
|
|
if (hkRegistry)
|
|
{
|
|
retStatus = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
|
|
(PWSTR)hkRegistry,
|
|
&QueryTable[0],
|
|
NULL,
|
|
NULL);
|
|
ZwCloseKey(hkRegistry);
|
|
}
|
|
if (!NT_SUCCESS(retStatus))
|
|
{
|
|
ASSERTGDI(FALSE, "Failed to get Monitor Settings\n");
|
|
return retStatus;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If we failed to get per monitor settings,
|
|
// try the hardware profile first and see if we can get parameters
|
|
// from that. If that fails, fall back to getting the system
|
|
// parameters.
|
|
//
|
|
|
|
for (k=1; k<=2; k++)
|
|
{
|
|
hkRegistry = DrvGetRegistryHandleFromDeviceMap(PhysDisp,
|
|
registryParam,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
gProtocolType);
|
|
|
|
if (hkRegistry == NULL)
|
|
{
|
|
TRACE_INIT(("Drv_Trace: GetDriverParams: failed - registry could not be opened\n"));
|
|
retStatus = STATUS_UNSUCCESSFUL;
|
|
}
|
|
else
|
|
{
|
|
retStatus = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
|
|
(PWSTR)hkRegistry,
|
|
&QueryTable[0],
|
|
NULL,
|
|
NULL);
|
|
|
|
ZwCloseKey(hkRegistry);
|
|
}
|
|
|
|
//
|
|
// If something failed for the hardware profile, try
|
|
// to get the global settings
|
|
// If everything is OK, just exit the loop
|
|
//
|
|
|
|
if (NT_SUCCESS(retStatus))
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
TRACE_INIT(("Drv_Trace: GetDriverParams: get hardware profile failed - try global settings\n"));
|
|
registryParam = DispDriverRegGlobal;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// AndreVa - do we still need this information passed back to the
|
|
// applet ?
|
|
//
|
|
|
|
//
|
|
// Other common fields to the DEVMODEs
|
|
//
|
|
|
|
if (NT_SUCCESS(retStatus))
|
|
{
|
|
//
|
|
// Lets check if the DEVMODE we got is all NULLs (like when
|
|
// the driver just got installed).
|
|
// If it is, the driver should be reconfigured
|
|
//
|
|
// We will only do this if we are NOT in BaseVideo, since the VGA
|
|
// BaseVideo driver need not be configured.
|
|
//
|
|
|
|
if ((attached) &&
|
|
(pdevmode->dmBitsPerPel == 0) &&
|
|
(pdevmode->dmPelsWidth == 0) &&
|
|
(pdevmode->dmPelsHeight == 0) &&
|
|
(pdevmode->dmDisplayFrequency == 0) &&
|
|
(pdevmode->dmDisplayFlags == 0) &&
|
|
(gbBaseVideo == FALSE))
|
|
{
|
|
DrvLogDisplayDriverEvent(MsgInvalidUsingDefaultMode);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Let's fill out all the other fields of the DEVMODE that ALWAYS
|
|
// need to be initialized.
|
|
//
|
|
|
|
if (NT_SUCCESS(retStatus))
|
|
{
|
|
//
|
|
// Set versions and size.
|
|
//
|
|
|
|
pdevmode->dmSpecVersion = DM_SPECVERSION;
|
|
pdevmode->dmDriverVersion = DM_SPECVERSION;
|
|
pdevmode->dmSize = sizeof(DEVMODEW);
|
|
|
|
//
|
|
// Currently, the logpixel value should not be changed on the fly.
|
|
// So once it has been read out of the registry at boot time, keep
|
|
// that same value and ignore the registry.
|
|
//
|
|
|
|
if (gdmLogPixels)
|
|
{
|
|
pdevmode->dmLogPixels = gdmLogPixels;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Get the devices pelDPI out of the registry
|
|
//
|
|
|
|
UNICODE_STRING us;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
NTSTATUS Status;
|
|
HANDLE hKey;
|
|
DWORD cbSize;
|
|
BYTE Buf[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(DWORD)];
|
|
|
|
pdevmode->dmLogPixels = 96;
|
|
|
|
//
|
|
// Look in the Hardware Profile for the current font size.
|
|
// If that fails, look in the global software location.
|
|
//
|
|
|
|
RtlInitUnicodeString(&us, L"\\Registry\\Machine\\System"
|
|
L"\\CurrentControlSet\\Hardware Profiles"
|
|
L"\\Current\\Software\\Fonts");
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&us,
|
|
OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = ZwOpenKey(&hKey, KEY_READ, &ObjectAttributes);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
RtlInitUnicodeString(&us, L"\\Registry\\Machine\\Software"
|
|
L"\\Microsoft\\Windows NT"
|
|
L"\\CurrentVersion\\FontDPI");
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&us,
|
|
OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = ZwOpenKey(&hKey, KEY_READ, &ObjectAttributes);
|
|
}
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
RtlInitUnicodeString(&us, L"LogPixels");
|
|
|
|
Status = ZwQueryValueKey(hKey,
|
|
&us,
|
|
KeyValuePartialInformation,
|
|
(PKEY_VALUE_PARTIAL_INFORMATION)Buf,
|
|
sizeof(Buf),
|
|
&cbSize);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
pdevmode->dmLogPixels =
|
|
*((PUSHORT)((PKEY_VALUE_PARTIAL_INFORMATION)Buf)->Data);
|
|
}
|
|
|
|
ZwCloseKey(hKey);
|
|
}
|
|
|
|
|
|
//
|
|
// For non high-res mode, let's force small font size so
|
|
// that various dialogs are not clipped out.
|
|
//
|
|
|
|
if (pdevmode->dmPelsHeight < 600)
|
|
{
|
|
pdevmode->dmLogPixels = 96;
|
|
}
|
|
|
|
gdmLogPixels = pdevmode->dmLogPixels;
|
|
|
|
}
|
|
|
|
//
|
|
// Set all the appropriate DEVMODE flags.
|
|
//
|
|
|
|
pdevmode->dmFields = DM_INTERNAL_VALID_FLAGS;
|
|
|
|
if (attached)
|
|
{
|
|
pdevmode->dmFields |= DM_POSITION;
|
|
}
|
|
|
|
if (pdevmode->dmDisplayFixedOutput != DMDFO_DEFAULT)
|
|
{
|
|
pdevmode->dmFields |= DM_DISPLAYFIXEDOUTPUT;
|
|
}
|
|
|
|
TRACE_INIT(("Drv_Trace: GetDriverParams: DEVMODE\n"));
|
|
dbgDumpDevmode(pdevmode);
|
|
|
|
|
|
if ((PhysDisp->stateFlags & DISPLAY_DEVICE_DISCONNECT) != 0 )
|
|
{
|
|
DEVMODEW disconnectDevmode;
|
|
|
|
disconnectDevmode.dmFields = 0;
|
|
UserGetDisconnectDeviceResolutionHint(&disconnectDevmode);
|
|
if (disconnectDevmode.dmFields & DM_PELSWIDTH && disconnectDevmode.dmFields & DM_PELSHEIGHT)
|
|
{
|
|
pdevmode->dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT;
|
|
pdevmode->dmPelsWidth = disconnectDevmode.dmPelsWidth;
|
|
pdevmode->dmPelsHeight = disconnectDevmode.dmPelsHeight;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRACE_INIT(("Drv_Trace: GetSetParms: Get failed\n\n"));
|
|
|
|
}
|
|
|
|
TRACE_INIT(("Drv_Trace: GetDriverParams: Exit\n\n"));
|
|
|
|
return (retStatus);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* DrvWriteDisplayDriverParameters
|
|
*
|
|
* Wites the resolution parameters to the registry.
|
|
*
|
|
* 13-Mar-1996 andreva created
|
|
\**************************************************************************/
|
|
|
|
NTSTATUS
|
|
DrvWriteDisplayDriverParameters(
|
|
ULONG RelativeTo,
|
|
PWSTR Path,
|
|
PDEVMODEW pdevmode,
|
|
BOOL bDetach)
|
|
{
|
|
ULONG i;
|
|
NTSTATUS retStatus = STATUS_SUCCESS;
|
|
DWORD data[NUM_DISPLAY_DRIVER_EXTRA];
|
|
DWORD numData = NUM_DISPLAY_DRIVER_EXTRA - 1;
|
|
DWORD dataval;
|
|
|
|
//
|
|
// If we are deteaching, shortcircuit and just write that value out.
|
|
//
|
|
|
|
if (bDetach)
|
|
{
|
|
dataval = 0;
|
|
|
|
ASSERTGDI(pdevmode == NULL, "Detach should have NULL DEVMODE\n");
|
|
|
|
return RtlWriteRegistryValue(RelativeTo,
|
|
Path,
|
|
(PWSTR)AttachedSettings[1],
|
|
REG_DWORD,
|
|
&dataval,
|
|
sizeof(DWORD));
|
|
}
|
|
|
|
//
|
|
// DM_POSITION determine whether or not we write the attach values
|
|
//
|
|
|
|
if (pdevmode->dmFields & DM_POSITION)
|
|
{
|
|
//
|
|
// Determine what we want to set the attached value to.
|
|
//
|
|
|
|
dataval = 1;
|
|
|
|
retStatus = RtlWriteRegistryValue(RelativeTo,
|
|
Path,
|
|
(PWSTR)AttachedSettings[1],
|
|
REG_DWORD,
|
|
&dataval,
|
|
sizeof(DWORD));
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Don't write the Position values if we are not attaching
|
|
//
|
|
|
|
numData -= 2;
|
|
}
|
|
|
|
|
|
//
|
|
// Write all the reset of the data
|
|
//
|
|
|
|
data[0] = pdevmode->dmBitsPerPel;
|
|
data[1] = pdevmode->dmPelsWidth;
|
|
data[2] = pdevmode->dmPelsHeight;
|
|
data[3] = pdevmode->dmDisplayFrequency;
|
|
data[4] = pdevmode->dmDisplayFlags;
|
|
data[5] = pdevmode->dmPanningWidth;
|
|
data[6] = pdevmode->dmPanningHeight;
|
|
data[7] = pdevmode->dmDisplayOrientation;
|
|
data[8] = pdevmode->dmDisplayFixedOutput;
|
|
data[9] = pdevmode->dmPosition.x;
|
|
data[10]= pdevmode->dmPosition.y;
|
|
|
|
for (i=0; NT_SUCCESS(retStatus) && (i < numData); i++)
|
|
{
|
|
retStatus = RtlWriteRegistryValue(RelativeTo,
|
|
Path,
|
|
(PWSTR)DefaultSettings[i],
|
|
REG_DWORD,
|
|
&data[i],
|
|
sizeof(DWORD));
|
|
}
|
|
|
|
if (NT_SUCCESS(retStatus) && pdevmode->dmDriverExtra)
|
|
{
|
|
retStatus = RtlWriteRegistryValue(RelativeTo,
|
|
Path,
|
|
(PWSTR)DefaultSettings[NUM_DISPLAY_DRIVER_EXTRA],
|
|
REG_BINARY,
|
|
((PUCHAR)pdevmode) + pdevmode->dmSize,
|
|
pdevmode->dmDriverExtra);
|
|
}
|
|
|
|
return retStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
DrvUpdateDisplayDriverParameters(
|
|
PGRAPHICS_DEVICE PhysDisp,
|
|
LPDEVMODEW pdevmodeInformation,
|
|
BOOL bDetach,
|
|
BOOL bUpdateMonitors)
|
|
{
|
|
HANDLE hkRegistry;
|
|
ULONG i;
|
|
NTSTATUS retStatus = STATUS_UNSUCCESSFUL;
|
|
DISP_DRIVER_REGISTRY_TYPE registryParam = DispDriverRegHardwareProfileCreate;
|
|
|
|
//
|
|
// Change the settings for the whole machine or for the user.
|
|
//
|
|
// CDS_GLOBAL is the default right now.
|
|
// When we store the settings on a per-user basis, then this flag
|
|
// will override that behaviour.
|
|
//
|
|
|
|
// if (ChangeGlobalSettings)
|
|
{
|
|
TRACE_INIT(("Drv_Trace: SetParms: Default Settings\n"));
|
|
|
|
//
|
|
// try the hardware profile first and see if we can get parameters
|
|
// from that. If that fails, fall back to getting the system
|
|
// parameters.
|
|
//
|
|
|
|
while (1)
|
|
{
|
|
hkRegistry = DrvGetRegistryHandleFromDeviceMap(PhysDisp,
|
|
registryParam,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
gProtocolType);
|
|
|
|
if (hkRegistry)
|
|
{
|
|
retStatus = DrvWriteDisplayDriverParameters(RTL_REGISTRY_HANDLE,
|
|
(LPWSTR) hkRegistry,
|
|
pdevmodeInformation,
|
|
bDetach);
|
|
ZwCloseKey(hkRegistry);
|
|
}
|
|
|
|
if ( (NT_SUCCESS(retStatus)) ||
|
|
(registryParam != DispDriverRegHardwareProfileCreate) )
|
|
break;
|
|
|
|
registryParam = DispDriverRegGlobal;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update per-monitor setting
|
|
//
|
|
if (NT_SUCCESS(retStatus) && bUpdateMonitors)
|
|
{
|
|
UpdateMonitorDevices();
|
|
|
|
for (i = 0; i < PhysDisp->numMonitorDevice; i++)
|
|
{
|
|
if (IS_ATTACHED_ACTIVE(PhysDisp->MonitorDevices[i].flag))
|
|
{
|
|
hkRegistry = DrvGetRegistryHandleFromDeviceMap(PhysDisp,
|
|
registryParam,
|
|
&PhysDisp->MonitorDevices[i].HwID,
|
|
NULL,
|
|
NULL,
|
|
gProtocolType);
|
|
if (hkRegistry)
|
|
{
|
|
// We don't need to check the status here. Per adapter settings plays
|
|
// as a backup
|
|
DrvWriteDisplayDriverParameters(RTL_REGISTRY_HANDLE,
|
|
(LPWSTR) hkRegistry,
|
|
pdevmodeInformation,
|
|
bDetach);
|
|
ZwCloseKey(hkRegistry);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TRACE_INIT(("Drv_Trace: SetParms: Exit\n\n"));
|
|
|
|
return retStatus;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* DrvGetHdevName
|
|
*
|
|
* Returns the name of the device associated with an HDEV so GetMonitorInfo
|
|
* can return this information
|
|
*
|
|
* 07-Jul-1998 andreva created
|
|
\**************************************************************************/
|
|
|
|
BOOL
|
|
DrvGetHdevName(
|
|
HDEV hdev,
|
|
PWCHAR DeviceName
|
|
)
|
|
{
|
|
PDEVOBJ pdo(hdev);
|
|
|
|
RtlCopyMemory(DeviceName,
|
|
pdo.ppdev->pGraphicsDevice->szWinDeviceName,
|
|
sizeof(pdo.ppdev->pGraphicsDevice->szWinDeviceName));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* DrvGetDeviceFromName
|
|
*
|
|
* Given the name of a device, returns a pointer to a structure describing
|
|
* the device.
|
|
*
|
|
* Specifying NULL tells the system to return the information for the default
|
|
* device on which the application is running.
|
|
*
|
|
* returns a PGRAPHICS_DEVICE
|
|
*
|
|
* *** NOTE
|
|
* If the caller requests Exclusive access, the caller must call back and
|
|
* release the access once the device is no longer used.
|
|
*
|
|
* 31-May-1994 andreva created
|
|
\**************************************************************************/
|
|
|
|
PGRAPHICS_DEVICE
|
|
DrvGetDeviceFromName(
|
|
PUNICODE_STRING pstrDeviceName,
|
|
MODE PreviousMode)
|
|
{
|
|
ULONG i = 0;
|
|
NTSTATUS status;
|
|
PGRAPHICS_DEVICE PhysDisp;
|
|
PDEVICE_OBJECT pDeviceObject = NULL;
|
|
UNICODE_STRING uCapturedString;
|
|
UNICODE_STRING uString;
|
|
BOOL fDisableSuccess;
|
|
|
|
TRACE_INIT(("Drv_Trace: GetDev: Enter"));
|
|
|
|
__try
|
|
{
|
|
if (PreviousMode == UserMode)
|
|
{
|
|
uCapturedString.Length = 0;
|
|
|
|
|
|
if (pstrDeviceName)
|
|
{
|
|
ProbeForRead(pstrDeviceName, sizeof(UNICODE_STRING), sizeof(CHAR));
|
|
uCapturedString = *pstrDeviceName;
|
|
}
|
|
|
|
if (uCapturedString.Length)
|
|
{
|
|
ProbeForRead(uCapturedString.Buffer,
|
|
uCapturedString.Length,
|
|
sizeof(BYTE));
|
|
}
|
|
else
|
|
{
|
|
|
|
#if DONT_CHECKIN
|
|
PDESKTOP pdesk = PtiCurrent()->rpdesk;
|
|
PDISPLAYINFO pDispInfo;
|
|
|
|
if (pdesk)
|
|
{
|
|
//
|
|
// Special case for boot-up time.
|
|
//
|
|
pDispInfo = pdesk->pDispInfo;
|
|
}
|
|
else
|
|
{
|
|
pDispInfo = G_TERM(pDispInfo);
|
|
}
|
|
|
|
RtlInitUnicodeString(&uCapturedString,
|
|
pDispInfo->pDevInfo->szWinDeviceName);
|
|
#endif
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERTGDI(pstrDeviceName >= (PUNICODE_STRING const)MM_USER_PROBE_ADDRESS,
|
|
"Bad kernel mode address\n");
|
|
uCapturedString = *pstrDeviceName;
|
|
}
|
|
|
|
//
|
|
// Look for an existing handle in our handle table.
|
|
// Start by looking for the VGACOMPATIBLE string, which is
|
|
// our VgaCompatible device
|
|
//
|
|
|
|
RtlInitUnicodeString(&uString, L"VGACOMPATIBLE");
|
|
|
|
if (RtlEqualUnicodeString(&uCapturedString,
|
|
&uString,
|
|
TRUE))
|
|
{
|
|
PhysDisp = &gFullscreenGraphicsDevice;
|
|
}
|
|
else
|
|
{
|
|
for (PhysDisp = gpGraphicsDeviceList;
|
|
PhysDisp;
|
|
PhysDisp = PhysDisp->pNextGraphicsDevice)
|
|
{
|
|
|
|
|
|
RtlInitUnicodeString(&uString,
|
|
&(PhysDisp->szWinDeviceName[0]));
|
|
|
|
if (RtlEqualUnicodeString(&uCapturedString,
|
|
&uString,
|
|
TRUE))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
PhysDisp = NULL;
|
|
}
|
|
|
|
|
|
if (PhysDisp == NULL)
|
|
{
|
|
WARNING("DrvGetDeviceFromName: Calling for a non-exsting device!");
|
|
return NULL;
|
|
}
|
|
|
|
|
|
TRACE_INIT((" - Exit\n"));
|
|
|
|
return PhysDisp;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* PruneModesByMonitors
|
|
*
|
|
* Read the EDID from regstry first. Retrieve the Monitor capabilities.
|
|
* And prune mode list based on Monitor capability.
|
|
\**************************************************************************/
|
|
|
|
int compModeCap(LPMODECAP pModeCap1, LPMODECAP pModeCap2)
|
|
{
|
|
if (pModeCap1->dmWidth != pModeCap2->dmWidth)
|
|
{
|
|
return (pModeCap1->dmWidth - pModeCap2->dmWidth);
|
|
}
|
|
return (pModeCap1->dmHeight - pModeCap2->dmHeight);
|
|
}
|
|
|
|
ULONG InsertModecapList(LPMODECAP pmc, LPMODECAP ModeCaps, ULONG numModeCaps)
|
|
{
|
|
ULONG i;
|
|
int comp = -1;
|
|
for (i = 0; i < numModeCaps; i++)
|
|
{
|
|
comp = compModeCap(pmc, &ModeCaps[i]);
|
|
if (comp > 0)
|
|
{
|
|
continue;
|
|
}
|
|
if (comp == 0)
|
|
{
|
|
//
|
|
// For duplicated entries, pick the bigger one
|
|
//
|
|
if (ModeCaps[i].freq < pmc->freq)
|
|
{
|
|
ModeCaps[i].freq = pmc->freq;
|
|
ModeCaps[i].MinVFreq = pmc->MinVFreq;
|
|
}
|
|
if (ModeCaps[i].MaxHFreq < pmc->MaxHFreq)
|
|
{
|
|
ModeCaps[i].MaxHFreq = pmc->MaxHFreq;
|
|
ModeCaps[i].MinHFreq = pmc->MinHFreq;
|
|
}
|
|
return numModeCaps;
|
|
}
|
|
|
|
//
|
|
// If buffer size reaches the limit, do nothing and return
|
|
//
|
|
if (numModeCaps >= MAX_MODE_CAPABILITY)
|
|
{
|
|
return numModeCaps;
|
|
}
|
|
|
|
RtlMoveMemory(&ModeCaps[i+1], &ModeCaps[i], (numModeCaps-i)*sizeof(MODECAP));
|
|
ModeCaps[i] = *pmc;
|
|
return (numModeCaps+1);
|
|
}
|
|
ModeCaps[numModeCaps] = *pmc;
|
|
return (numModeCaps+1);
|
|
}
|
|
|
|
ULONG xwtol(LPWSTR wptr)
|
|
{
|
|
for (ULONG v = 0;
|
|
(*wptr >= L'0' && *wptr <= L'9') || *wptr == L' ';
|
|
*wptr++)
|
|
{
|
|
if (*wptr != L' ')
|
|
v = v*10 + (*wptr - L'0');
|
|
}
|
|
return v;
|
|
}
|
|
|
|
BOOL ParseModeCap(LPWSTR wstr, LPMODECAP pmc, BOOL bFreq)
|
|
{
|
|
LPWSTR wptr1, wptr2, wptr3;
|
|
ULONG v[4] = {0, 0xFFFFFFFF, 0, 0xFFFFFFFF}, i;
|
|
for (wptr1 = wptr2 = wstr, i = 0;
|
|
wptr2 != NULL && i < 4;
|
|
wptr1 = wptr2+1, i++)
|
|
{
|
|
wptr2 = wcschr(wptr1, L',');
|
|
if (wptr2 != NULL)
|
|
{
|
|
*wptr2 = 0;
|
|
}
|
|
if (bFreq)
|
|
{
|
|
wptr3 = wcschr(wptr1, L'-');
|
|
if (wptr3 != NULL)
|
|
{
|
|
*wptr3 = 0;
|
|
v[i] = xwtol(wptr1);
|
|
wptr1 = wptr3+1;
|
|
}
|
|
else
|
|
{
|
|
v[i] = 0;
|
|
}
|
|
i++;
|
|
}
|
|
v[i] = xwtol(wptr1);
|
|
}
|
|
if (bFreq)
|
|
{
|
|
pmc->MinVFreq = max(pmc->MinVFreq, v[2]);
|
|
pmc->freq = min(pmc->freq, v[3]);
|
|
pmc->MinHFreq = max(pmc->MinHFreq, v[0] * 1000);
|
|
pmc->MaxHFreq = min(pmc->MaxHFreq, v[1] * 1000);
|
|
}
|
|
else
|
|
{
|
|
if (v[0] == 0 || v[1] == 0xFFFFFFFF)
|
|
{
|
|
return FALSE;
|
|
}
|
|
pmc->dmWidth = v[0];
|
|
pmc->dmHeight = v[1];
|
|
pmc->freq = v[2];
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
ULONG GetMonitorCapabilityFromInf(PDEVICE_OBJECT pdo, LPMODECAP ModeCaps)
|
|
{
|
|
HANDLE hkRegistry, hkRegistry1, hkRegistry2;
|
|
|
|
if ( !NT_SUCCESS(IoOpenDeviceRegistryKey(pdo, PLUGPLAY_REGKEY_DRIVER, KEY_READ, &hkRegistry)) )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
ULONG numModeCaps = 0;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING UnicodeString;
|
|
|
|
RtlInitUnicodeString(&UnicodeString, L"MODES");
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&UnicodeString,
|
|
OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
|
|
hkRegistry,
|
|
NULL);
|
|
if ( NT_SUCCESS (ZwOpenKey(&hkRegistry1, KEY_READ, &ObjectAttributes)) )
|
|
{
|
|
WCHAR data[128], buf[128];
|
|
ULONG i = 0, size;
|
|
MODECAP mc;
|
|
|
|
while (NT_SUCCESS(ZwEnumerateKey(hkRegistry1,
|
|
i,
|
|
KeyBasicInformation,
|
|
data,
|
|
sizeof(data),
|
|
&size)))
|
|
{
|
|
i++;
|
|
|
|
UnicodeString.Buffer = ((PKEY_BASIC_INFORMATION) data)->Name;
|
|
UnicodeString.Length = (USHORT) ((PKEY_BASIC_INFORMATION) data)->NameLength;
|
|
UnicodeString.MaximumLength = (USHORT) ((PKEY_BASIC_INFORMATION) data)->NameLength;
|
|
|
|
//
|
|
// 1024, 768
|
|
//
|
|
wcsncpy(buf, UnicodeString.Buffer, min(UnicodeString.Length, sizeof(buf))/sizeof(WCHAR));
|
|
if (UnicodeString.Length < sizeof(buf) )
|
|
buf[UnicodeString.Length/sizeof(WCHAR)] = 0;
|
|
buf[sizeof(buf)/sizeof(WCHAR)-1] = 0;
|
|
if (!ParseModeCap(buf, &mc, FALSE))
|
|
continue;
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&UnicodeString,
|
|
OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
|
|
hkRegistry1,
|
|
NULL);
|
|
|
|
if (NT_SUCCESS (ZwOpenKey(&hkRegistry2,
|
|
MAXIMUM_ALLOWED,
|
|
&ObjectAttributes)))
|
|
{
|
|
WCHAR modeStr[] = L"Mode1";
|
|
BOOL bSucceed = FALSE;
|
|
|
|
for (ULONG j = 0; j < 9; j++)
|
|
{
|
|
RtlInitUnicodeString(&UnicodeString, modeStr);
|
|
|
|
if (NT_SUCCESS(ZwQueryValueKey(hkRegistry2,
|
|
&UnicodeString,
|
|
KeyValueFullInformation,
|
|
data,
|
|
sizeof(data),
|
|
&size)) )
|
|
{
|
|
wcsncpy(buf,
|
|
(LPWSTR)((PBYTE)data + ((PKEY_VALUE_FULL_INFORMATION)data)->DataOffset),
|
|
sizeof(buf)/sizeof(WCHAR)-1);
|
|
//
|
|
// MinHFreq MaxHFreq MinVFreq MaxVFreq
|
|
// 31.0 - 82.0, 55.0 - 100.0, +,+
|
|
//
|
|
mc.MinVFreq = MIN_REFRESH_RATE;
|
|
mc.freq = 0xFFFFFFFF;
|
|
mc.MinHFreq = 0;
|
|
mc.MaxHFreq = 0xFFFFFFFF;
|
|
if (ParseModeCap(buf, &mc, TRUE))
|
|
{
|
|
numModeCaps = InsertModecapList(&mc, ModeCaps, numModeCaps);
|
|
}
|
|
|
|
bSucceed = TRUE;
|
|
}
|
|
else if (bSucceed)
|
|
{
|
|
break;
|
|
}
|
|
|
|
modeStr[4]++;
|
|
}
|
|
ZwCloseKey(hkRegistry2);
|
|
}
|
|
}
|
|
|
|
ZwCloseKey(hkRegistry1);
|
|
}
|
|
ZwCloseKey(hkRegistry);
|
|
return numModeCaps;
|
|
}
|
|
|
|
BOOL GetRegEDID(PDEVICE_OBJECT pdo, PBYTE pBuffer, PBYTE *ppEdid)
|
|
{
|
|
HANDLE hkRegistry;
|
|
NTSTATUS status;
|
|
|
|
// Get EDID information from registry
|
|
if ( !NT_SUCCESS(IoOpenDeviceRegistryKey(pdo, PLUGPLAY_REGKEY_DEVICE, KEY_READ, &hkRegistry)) )
|
|
{
|
|
TRACE_INIT(("Drv_Trace: GetMonitorCapability: Failed to retrieve EDID\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
UNICODE_STRING UnicodeString;
|
|
ULONG cbSize;
|
|
RtlInitUnicodeString(&UnicodeString, L"EDID");
|
|
|
|
status = ZwQueryValueKey(hkRegistry,
|
|
&UnicodeString,
|
|
KeyValueFullInformation,
|
|
pBuffer,
|
|
400,
|
|
&cbSize);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
if ( (((PKEY_VALUE_FULL_INFORMATION)pBuffer)->DataLength) < 256)
|
|
status = STATUS_UNSUCCESSFUL;
|
|
else
|
|
*ppEdid = (PBYTE)(pBuffer + ((PKEY_VALUE_FULL_INFORMATION)pBuffer)->DataOffset);
|
|
}
|
|
(VOID)ZwCloseKey(hkRegistry);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL GetValuesFromInf(PDEVICE_OBJECT pdo, ULONG op, LPMODECAP pmc)
|
|
{
|
|
if (op > 1)
|
|
return FALSE;
|
|
|
|
HANDLE hkRegistry;
|
|
static LPWSTR ValueName[] = {
|
|
L"PreferredMode",
|
|
L"ClearType"
|
|
};
|
|
|
|
if ( !NT_SUCCESS(IoOpenDeviceRegistryKey(pdo, PLUGPLAY_REGKEY_DRIVER, KEY_READ, &hkRegistry)) )
|
|
return FALSE;
|
|
|
|
UNICODE_STRING UnicodeString;
|
|
WCHAR data[128], buf[128];
|
|
ULONG size;
|
|
|
|
// Assuming return failure
|
|
BOOL bRet = FALSE;
|
|
RtlInitUnicodeString(&UnicodeString, ValueName[op]);
|
|
if (NT_SUCCESS(ZwQueryValueKey(hkRegistry,
|
|
&UnicodeString,
|
|
KeyValueFullInformation,
|
|
data,
|
|
sizeof(data),
|
|
&size)) )
|
|
{
|
|
switch (op)
|
|
{
|
|
case 0: // GetPreferredMode
|
|
wcsncpy(buf,
|
|
(LPWSTR)((PBYTE)data + ((PKEY_VALUE_FULL_INFORMATION)data)->DataOffset),
|
|
sizeof(buf)/sizeof(WCHAR)-1);
|
|
// 1024,768,60
|
|
if (ParseModeCap(buf, pmc, FALSE))
|
|
{
|
|
if (pmc->dmWidth != 0 && pmc->dmHeight != 0 && pmc->freq != 0)
|
|
bRet = TRUE;
|
|
}
|
|
break;
|
|
case 1: // Get ClearType Capability
|
|
pmc->dmWidth = *(PULONG) ( (PBYTE)data + ((PKEY_VALUE_FULL_INFORMATION)data)->DataOffset );
|
|
bRet = TRUE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
ZwCloseKey(hkRegistry);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* GetDefaultFrequencyRange
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* Returns default frequency range for the monitor.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* pFrequencyRange - Points to storage for monitor frequency limits.
|
|
*
|
|
* Returns:
|
|
*
|
|
* Default monitor frequency limits returned in pFrequencyRange.
|
|
*
|
|
* 08-Oct-1999 mmacie created
|
|
\**************************************************************************/
|
|
|
|
VOID
|
|
GetDefaultFrequencyRange(
|
|
OUT PFREQUENCY_RANGE pFrequencyRange
|
|
)
|
|
{
|
|
ASSERT(NULL != pFrequencyRange);
|
|
|
|
pFrequencyRange->ulMinVerticalRate = MIN_REFRESH_RATE;
|
|
pFrequencyRange->ulMaxVerticalRate = 0xffffffff;
|
|
pFrequencyRange->ulMinHorizontalRate = 0;
|
|
pFrequencyRange->ulMaxHorizontalRate = 0xffffffff;
|
|
pFrequencyRange->ulMinPixelClock = 0;
|
|
pFrequencyRange->ulMaxPixelClock = 0xffffffff;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* GetMonitorCapability2
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* Parsing routine for EDID Version 2.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* pEdid2 - Points to EDID Version 2 data.
|
|
* pModeCaps - Points to storage for monitor cap details.
|
|
* pFrequencyRange - Points to storage for monitor frequency limits.
|
|
*
|
|
* Returns:
|
|
*
|
|
* Number of mode capabilites.
|
|
* - Mode cap data returned in pModeCaps.
|
|
* - Monitor frequency limits returned in pFrequencyRange.
|
|
*
|
|
* 21-Sep-1999 mmacie created
|
|
\**************************************************************************/
|
|
|
|
ULONG
|
|
GetMonitorCapability2(
|
|
IN PEDID2 pEdid2,
|
|
OUT PMODECAP pModeCaps,
|
|
OUT PFREQUENCY_RANGE pFrequencyRange
|
|
)
|
|
{
|
|
ULONG ulNumberOfModeCaps;
|
|
ULONG ulNumberOfLuminanceTables;
|
|
ULONG ulNumberOfFrequencyRanges;
|
|
ULONG ulNumberOfDetailTimingRanges;
|
|
ULONG ulNumberOfTimingCodes;
|
|
ULONG ulNumberOfDetailTimings;
|
|
ULONG ulEdidOffset;
|
|
ULONG ulTemp;
|
|
|
|
ASSERT(NULL != pEdid2);
|
|
ASSERT(NULL != pModeCaps);
|
|
ASSERT(NULL != pFrequencyRange);
|
|
ASSERT(0x20 == pEdid2->ucEdidVersionRevision); // Make sure EDID Version 2
|
|
|
|
//
|
|
// Parse map of timings.
|
|
//
|
|
|
|
ulNumberOfLuminanceTables = (pEdid2->ucaMapOfTiming[0] & EDID2_MOT0_LUMINANCE_TABLE_MASK) >>
|
|
EDID2_MOT0_LUMINANCE_TABLE_SHIFT;
|
|
|
|
ulNumberOfFrequencyRanges = (pEdid2->ucaMapOfTiming[0] & EDID2_MOT0_FREQUENCY_RANGE_MASK) >>
|
|
EDID2_MOT0_FREQUENCY_RANGE_SHIFT;
|
|
|
|
ulNumberOfDetailTimingRanges = (pEdid2->ucaMapOfTiming[0] & EDID2_MOT0_DETAIL_TIMING_RANGE_MASK) >>
|
|
EDID2_MOT0_DETAIL_TIMING_RANGE_SHIFT;
|
|
|
|
ulNumberOfTimingCodes = (pEdid2->ucaMapOfTiming[1] & EDID2_MOT1_TIMING_CODE_MASK) >>
|
|
EDID2_MOT1_TIMING_CODE_SHIFT;
|
|
|
|
ulNumberOfDetailTimings = (pEdid2->ucaMapOfTiming[1] & EDID2_MOT1_DETAIL_TIMING_MASK) >>
|
|
EDID2_MOT1_DETAIL_TIMING_SHIFT;
|
|
|
|
TRACE_INIT(("win32k!GetMonitorCapability2: NumberOfLuminanceTables = %lu\n", ulNumberOfLuminanceTables));
|
|
TRACE_INIT(("win32k!GetMonitorCapability2: NumberOfFrequencyRanges = %lu\n", ulNumberOfFrequencyRanges));
|
|
TRACE_INIT(("win32k!GetMonitorCapability2: NumberOfDetailTimingRanges = %lu\n", ulNumberOfDetailTimingRanges));
|
|
TRACE_INIT(("win32k!GetMonitorCapability2: NumberOfDetailTimingCodes = %lu\n", ulNumberOfTimingCodes));
|
|
TRACE_INIT(("win32k!GetMonitorCapability2: NumberOfDetailTimings = %lu\n", ulNumberOfDetailTimings));
|
|
|
|
//
|
|
// Check for bad EDIDs.
|
|
//
|
|
|
|
if ((ulNumberOfLuminanceTables > EDID2_MAX_LUMINANCE_TABLES) ||
|
|
(ulNumberOfFrequencyRanges > EDID2_MAX_FREQUENCY_RANGES) ||
|
|
(ulNumberOfDetailTimingRanges > EDID2_MAX_DETAIL_TIMING_RANGES) ||
|
|
(ulNumberOfTimingCodes > EDID2_MAX_TIMING_CODES) ||
|
|
(ulNumberOfDetailTimings > EDID2_MAX_DETAIL_TIMINGS))
|
|
{
|
|
WARNING("Bad EDID2\n");
|
|
GetDefaultFrequencyRange(pFrequencyRange);
|
|
return 0;
|
|
}
|
|
|
|
ulEdidOffset = EDID2_LUMINANCE_TABLE_OFFSET;
|
|
|
|
//
|
|
// Move over luminance tables.
|
|
//
|
|
|
|
if (ulNumberOfLuminanceTables)
|
|
{
|
|
PUCHAR pucLuminanceTable;
|
|
|
|
for (ulTemp = 0; ulTemp < ulNumberOfLuminanceTables; ulTemp++)
|
|
{
|
|
//
|
|
// Check for bad EDIDs.
|
|
//
|
|
|
|
if (ulEdidOffset >= (sizeof (EDID2) - 1))
|
|
{
|
|
WARNING("Bad EDID2\n");
|
|
GetDefaultFrequencyRange(pFrequencyRange);
|
|
return 0;
|
|
}
|
|
|
|
pucLuminanceTable = (PUCHAR)pEdid2 + ulEdidOffset;
|
|
|
|
ulEdidOffset += (((*pucLuminanceTable & EDID2_LT0_SUB_CHANNELS_FLAG) ? 3 : 1) *
|
|
((*pucLuminanceTable & EDID2_LT0_ENTRIES_MASK) >> EDID2_LT0_ENTRIES_SHIFT) + 1);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Parse frequency ranges.
|
|
//
|
|
|
|
if (ulNumberOfFrequencyRanges)
|
|
{
|
|
PEDID2_FREQUENCY_RANGE pEdidRange;
|
|
ULONG ulRate;
|
|
|
|
//
|
|
// Currently the mode prunning code cannot handle multiple
|
|
// frequency ranges but EDID Version 2 can supply them.
|
|
// We're hacking around here by merging all of them into a single
|
|
// "super frequency range".
|
|
//
|
|
|
|
//
|
|
// Make sure we pick up the first range.
|
|
//
|
|
|
|
pFrequencyRange->ulMinVerticalRate = 0xffffffff;
|
|
pFrequencyRange->ulMaxVerticalRate = 0;
|
|
pFrequencyRange->ulMinHorizontalRate = 0xffffffff;
|
|
pFrequencyRange->ulMaxHorizontalRate = 0;
|
|
pFrequencyRange->ulMinPixelClock = 0xffffffff;
|
|
pFrequencyRange->ulMaxPixelClock = 0;
|
|
|
|
for (ulTemp = 0; ulTemp < ulNumberOfFrequencyRanges; ulTemp++,
|
|
ulEdidOffset += sizeof (EDID2_FREQUENCY_RANGE))
|
|
{
|
|
//
|
|
// Check for bad EDIDs.
|
|
//
|
|
|
|
if (ulEdidOffset >= (sizeof (EDID2) - sizeof (EDID2_FREQUENCY_RANGE)))
|
|
{
|
|
WARNING("Bad EDID2\n");
|
|
GetDefaultFrequencyRange(pFrequencyRange);
|
|
return 0;
|
|
}
|
|
|
|
pEdidRange = (PEDID2_FREQUENCY_RANGE)((PUCHAR)pEdid2 + ulEdidOffset);
|
|
|
|
ulRate = (ULONG)(pEdidRange->ucMinFrameFieldRateBits9_2) << 2;
|
|
ulRate += (pEdidRange->ucFrameFieldLineRatesBits1_0 & 0xc0) >> 6;
|
|
|
|
if (ulRate < pFrequencyRange->ulMinVerticalRate)
|
|
pFrequencyRange->ulMinVerticalRate = ulRate;
|
|
|
|
ulRate = (ULONG)(pEdidRange->ucMaxFrameFieldRateBits9_2) << 2;
|
|
ulRate += (pEdidRange->ucFrameFieldLineRatesBits1_0 & 0x30) >> 4;
|
|
|
|
if (ulRate > pFrequencyRange->ulMaxVerticalRate)
|
|
pFrequencyRange->ulMaxVerticalRate = ulRate;
|
|
|
|
ulRate = (ULONG)(pEdidRange->ucMinLineRateBits9_2) << 2;
|
|
ulRate += (pEdidRange->ucFrameFieldLineRatesBits1_0 & 0x0c) >> 2;
|
|
ulRate *= 1000;
|
|
|
|
if (ulRate < pFrequencyRange->ulMinHorizontalRate)
|
|
pFrequencyRange->ulMinHorizontalRate = ulRate;
|
|
|
|
ulRate = (ULONG)(pEdidRange->ucMaxLineRateBits9_2) << 2;
|
|
ulRate += (pEdidRange->ucFrameFieldLineRatesBits1_0 & 0x03);
|
|
ulRate *= 1000;
|
|
|
|
if (ulRate > pFrequencyRange->ulMaxHorizontalRate)
|
|
pFrequencyRange->ulMaxHorizontalRate = ulRate;
|
|
|
|
ulRate = (ULONG)((pEdidRange->ucPixelRatesBits11_8) & 0xf0) << 4;
|
|
ulRate += pEdidRange->ucMinPixelRateBits7_0;
|
|
ulRate *= 1000000;
|
|
|
|
if (ulRate < pFrequencyRange->ulMinPixelClock)
|
|
pFrequencyRange->ulMinPixelClock = ulRate;
|
|
|
|
ulRate = (ULONG)((pEdidRange->ucPixelRatesBits11_8) & 0x0f) << 8;
|
|
ulRate += pEdidRange->ucMaxPixelRateBits7_0;
|
|
ulRate *= 1000000;
|
|
|
|
if (ulRate > pFrequencyRange->ulMaxPixelClock)
|
|
pFrequencyRange->ulMaxPixelClock = ulRate;
|
|
|
|
//
|
|
// Check for bad EDIDs.
|
|
//
|
|
|
|
if ((pFrequencyRange->ulMinVerticalRate > pFrequencyRange->ulMaxVerticalRate) ||
|
|
(pFrequencyRange->ulMinHorizontalRate > pFrequencyRange->ulMaxHorizontalRate) ||
|
|
(pFrequencyRange->ulMinPixelClock > pFrequencyRange->ulMaxPixelClock) ||
|
|
(pFrequencyRange->ulMaxVerticalRate < 60) ||
|
|
(pFrequencyRange->ulMaxHorizontalRate < (60*480)) ||
|
|
(pFrequencyRange->ulMaxPixelClock < (60*480*640))
|
|
)
|
|
{
|
|
WARNING("Bad EDID2\n");
|
|
GetDefaultFrequencyRange(pFrequencyRange);
|
|
}
|
|
}
|
|
}
|
|
|
|
ulNumberOfModeCaps = 0;
|
|
|
|
//
|
|
// Parse detail timing ranges.
|
|
//
|
|
|
|
if (ulNumberOfDetailTimingRanges)
|
|
{
|
|
PEDID2_DETAIL_TIMING_RANGE pEdidRange;
|
|
MODECAP modeCap;
|
|
ULONG ulHorizontalTotal;
|
|
ULONG ulVerticalTotal;
|
|
ULONG ulPixelClock;
|
|
|
|
//
|
|
// Currently the mode prunning code cannot handle detailed
|
|
// timing ranges but EDID Version 2 can supply them.
|
|
// We're hacking around here by taking maximum values only.
|
|
//
|
|
|
|
modeCap.MinVFreq = MIN_REFRESH_RATE;
|
|
modeCap.MinHFreq = 0;
|
|
modeCap.MaxHFreq = 0xffffffff;
|
|
|
|
for (ulTemp = 0; ulTemp < ulNumberOfDetailTimingRanges; ulTemp++,
|
|
ulEdidOffset += sizeof (EDID2_DETAIL_TIMING_RANGE))
|
|
{
|
|
//
|
|
// Check for bad EDIDs.
|
|
//
|
|
|
|
if (ulEdidOffset >= (sizeof (EDID2) - sizeof (EDID2_DETAIL_TIMING_RANGE)))
|
|
{
|
|
WARNING("Bad EDID2\n");
|
|
GetDefaultFrequencyRange(pFrequencyRange);
|
|
return 0;
|
|
}
|
|
|
|
pEdidRange = (PEDID2_DETAIL_TIMING_RANGE)((PUCHAR)pEdid2 + ulEdidOffset);
|
|
|
|
modeCap.dmWidth = (ULONG)(pEdidRange->ucActiveHighBits & 0xf0) << 4;
|
|
modeCap.dmWidth += pEdidRange->ucHorizontalActiveLowByte;
|
|
|
|
modeCap.dmHeight = (ULONG)(pEdidRange->ucActiveHighBits & 0x0f) << 8;
|
|
modeCap.dmHeight += pEdidRange->ucVerticalActiveLowByte;
|
|
|
|
ulHorizontalTotal = (ULONG)(pEdidRange->ucMaxBlankHighBits & 0xf0) << 4;
|
|
ulHorizontalTotal += pEdidRange->ucMaxHorizontalBlankLowByte + modeCap.dmWidth;
|
|
|
|
ulVerticalTotal = (ULONG)(pEdidRange->ucMaxBlankHighBits & 0x0f) << 8;
|
|
ulVerticalTotal += pEdidRange->ucMaxVerticalBlankLowByte + modeCap.dmHeight;
|
|
|
|
//
|
|
// Make sure we won't divide by zero in case of bad EDID.
|
|
//
|
|
|
|
if ((0 == ulHorizontalTotal) || (0 == ulVerticalTotal))
|
|
{
|
|
WARNING("Bad EDID2\n");
|
|
GetDefaultFrequencyRange(pFrequencyRange);
|
|
return 0;
|
|
}
|
|
|
|
ulPixelClock = (ULONG)(pEdidRange->usMaxPixelClock) * 10000;
|
|
|
|
//
|
|
// Calculate refresh rate rounding to the nearest integer.
|
|
//
|
|
|
|
modeCap.freq = (((10 * ulPixelClock) / (ulHorizontalTotal * ulVerticalTotal)) + 5) / 10;
|
|
|
|
if (pEdidRange->ucFlags & EDID2_DT_INTERLACED)
|
|
modeCap.freq /= 2;
|
|
|
|
TRACE_INIT(("win32k!GetMonitorCapability2: Detailed range %lux%lu at %luHz\n",
|
|
modeCap.dmWidth, modeCap.dmHeight, modeCap.freq));
|
|
|
|
ulNumberOfModeCaps = InsertModecapList(&modeCap, pModeCaps, ulNumberOfModeCaps);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Parse timing codes.
|
|
//
|
|
|
|
if (ulNumberOfTimingCodes)
|
|
{
|
|
PEDID2_TIMING_CODE pTimingCode;
|
|
MODECAP modeCap;
|
|
|
|
modeCap.MinVFreq = MIN_REFRESH_RATE;
|
|
modeCap.MinHFreq = 0;
|
|
modeCap.MaxHFreq = 0xffffffff;
|
|
|
|
for (ulTemp = 0; ulTemp < ulNumberOfTimingCodes; ulTemp++,
|
|
ulEdidOffset += sizeof (EDID2_TIMING_CODE))
|
|
{
|
|
//
|
|
// Check for bad EDIDs.
|
|
//
|
|
|
|
if (ulEdidOffset >= (sizeof (EDID2) - sizeof (EDID2_TIMING_CODE)))
|
|
{
|
|
WARNING("Bad EDID2\n");
|
|
GetDefaultFrequencyRange(pFrequencyRange);
|
|
return 0;
|
|
}
|
|
|
|
pTimingCode = (PEDID2_TIMING_CODE)((PUCHAR)pEdid2 + ulEdidOffset);
|
|
|
|
modeCap.dmWidth = 16 * pTimingCode->ucHorizontalActive + 256;
|
|
modeCap.dmHeight = (100 * modeCap.dmWidth) / pTimingCode->ucAspectRatio;
|
|
modeCap.freq = pTimingCode->ucRefreshRate;
|
|
|
|
if (pTimingCode->ucFlags & EDID2_TC_INTERLACED)
|
|
modeCap.freq /= 2;
|
|
|
|
TRACE_INIT(("win32k!GetMonitorCapability2: Timing code %lux%lu at %luHz\n",
|
|
modeCap.dmWidth, modeCap.dmHeight, modeCap.freq));
|
|
|
|
ulNumberOfModeCaps = InsertModecapList(&modeCap, pModeCaps, ulNumberOfModeCaps);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Parse detail timings.
|
|
//
|
|
|
|
if (ulNumberOfDetailTimings)
|
|
{
|
|
PEDID2_DETAIL_TIMING pDetailTiming;
|
|
MODECAP modeCap;
|
|
ULONG ulHorizontalTotal;
|
|
ULONG ulVerticalTotal;
|
|
ULONG ulPixelClock;
|
|
|
|
modeCap.MinVFreq = MIN_REFRESH_RATE;
|
|
modeCap.MinHFreq = 0;
|
|
modeCap.MaxHFreq = 0xffffffff;
|
|
|
|
for (ulTemp = 0; ulTemp < ulNumberOfDetailTimings; ulTemp++,
|
|
ulEdidOffset += sizeof (EDID2_DETAIL_TIMING))
|
|
{
|
|
//
|
|
// Check for bad EDIDs.
|
|
//
|
|
|
|
if (ulEdidOffset >= (sizeof (EDID2) - sizeof (EDID2_DETAIL_TIMING)))
|
|
{
|
|
WARNING("Bad EDID2\n");
|
|
GetDefaultFrequencyRange(pFrequencyRange);
|
|
return 0;
|
|
}
|
|
|
|
pDetailTiming = (PEDID2_DETAIL_TIMING)((PUCHAR)pEdid2 + ulEdidOffset);
|
|
|
|
modeCap.dmWidth = (ULONG)(pDetailTiming->ucHorizontalHighBits & 0xf0) << 4;
|
|
modeCap.dmWidth += pDetailTiming->ucHorizontalActiveLowByte;
|
|
|
|
modeCap.dmHeight = (ULONG)(pDetailTiming->ucVerticalHighBits & 0xf0) << 4;
|
|
modeCap.dmHeight += pDetailTiming->ucVerticalActiveLowByte;
|
|
|
|
ulHorizontalTotal = (ULONG)(pDetailTiming->ucHorizontalHighBits & 0x0f) << 8;
|
|
ulHorizontalTotal += pDetailTiming->ucHorizontalBlankLowByte + modeCap.dmWidth;
|
|
|
|
ulVerticalTotal = (ULONG)(pDetailTiming->ucVerticalHighBits & 0x0f) << 8;
|
|
ulVerticalTotal += pDetailTiming->ucVerticalBlankLowByte + modeCap.dmHeight;
|
|
|
|
//
|
|
// Make sure we won't divide by zero in case of bad EDID.
|
|
//
|
|
|
|
if ((0 == ulHorizontalTotal) || (0 == ulVerticalTotal))
|
|
{
|
|
WARNING("Bad EDID2\n");
|
|
GetDefaultFrequencyRange(pFrequencyRange);
|
|
return 0;
|
|
}
|
|
|
|
ulPixelClock = (ULONG)(pDetailTiming->usPixelClock) * 10000;
|
|
|
|
//
|
|
// Calculate refresh rate rounding to the nearest integer.
|
|
//
|
|
|
|
modeCap.freq = (((10 * ulPixelClock) / (ulHorizontalTotal * ulVerticalTotal)) + 5) / 10;
|
|
|
|
if (pDetailTiming->ucFlags & EDID2_DT_INTERLACED)
|
|
modeCap.freq /= 2;
|
|
|
|
TRACE_INIT(("win32k!GetMonitorCapability2: Detailed timing %lux%lu at %luHz\n",
|
|
modeCap.dmWidth, modeCap.dmHeight, modeCap.freq));
|
|
|
|
ulNumberOfModeCaps = InsertModecapList(&modeCap, pModeCaps, ulNumberOfModeCaps);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Define monitor frequency limits in case EDID didn't have it.
|
|
//
|
|
|
|
if (0 == ulNumberOfFrequencyRanges)
|
|
{
|
|
GetDefaultFrequencyRange(pFrequencyRange);
|
|
}
|
|
|
|
TRACE_INIT(("win32k!GetMonitorCapability2: MinVerticalRate = %lu\n", pFrequencyRange->ulMinVerticalRate));
|
|
TRACE_INIT(("win32k!GetMonitorCapability2: MaxVerticalRate = %lu\n", pFrequencyRange->ulMaxVerticalRate));
|
|
TRACE_INIT(("win32k!GetMonitorCapability2: MinHorizontalRate = %lu\n", pFrequencyRange->ulMinHorizontalRate));
|
|
TRACE_INIT(("win32k!GetMonitorCapability2: MaxHorizontalRate = %lu\n", pFrequencyRange->ulMaxHorizontalRate));
|
|
TRACE_INIT(("win32k!GetMonitorCapability2: MinPixelClock = %lu\n", pFrequencyRange->ulMinPixelClock));
|
|
TRACE_INIT(("win32k!GetMonitorCapability2: MaxPixelClock = %lu\n", pFrequencyRange->ulMaxPixelClock));
|
|
|
|
//
|
|
// Cascade refresh rates.
|
|
//
|
|
|
|
for (ulTemp = ulNumberOfModeCaps; ulTemp > 1; ulTemp--)
|
|
{
|
|
if (pModeCaps[ulTemp - 2].freq < pModeCaps[ulTemp - 1].freq)
|
|
pModeCaps[ulTemp - 2].freq = pModeCaps[ulTemp - 1].freq;
|
|
}
|
|
|
|
return ulNumberOfModeCaps;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* GetMonitorCapability1
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* Parsing routine for EDID Version 1.x
|
|
*
|
|
* Arguments:
|
|
*
|
|
* pEdid - Points to EDID Version 1 data.
|
|
* pModeCaps - Points to storage for monitor cap details.
|
|
* pFrequencyRange - Points to storage for monitor frequency limits.
|
|
*
|
|
* Returns:
|
|
*
|
|
* Number of mode capabilites.
|
|
* - Mode cap data returned in pModeCaps.
|
|
* - Monitor frequency limits returned in pFrequencyRange.
|
|
*
|
|
* 21-Sep-1999 dennyd created
|
|
\**************************************************************************/
|
|
|
|
ULONG
|
|
GetMonitorCapability1(
|
|
IN PBYTE pEdid,
|
|
OUT PMODECAP pModeCaps,
|
|
OUT PFREQUENCY_RANGE pFrequencyRange
|
|
)
|
|
{
|
|
ULONG numModeCaps = 0;
|
|
MODECAP mc;
|
|
PBYTE ptr;
|
|
BYTE c;
|
|
int i;
|
|
int r1[] = {1, 4, 5, 16}, r2[] = {1, 3, 4, 9};
|
|
UCHAR ucaEdid1Header[] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00};
|
|
|
|
GetDefaultFrequencyRange(pFrequencyRange);
|
|
|
|
//
|
|
// Check if EDID Version 1.
|
|
//
|
|
|
|
for (i = 0; i < (sizeof (ucaEdid1Header) / sizeof (UCHAR)); i++)
|
|
{
|
|
if (pEdid[i] != ucaEdid1Header[i])
|
|
return 0;
|
|
}
|
|
|
|
// Handle established timings
|
|
MODECAP mcArray[] = {{1280, 1024, 75},
|
|
{1024, 768, 75}, {1024, 768, 70}, {1024, 768, 60}, {1024, 768, 87},
|
|
{800, 600, 75}, {800, 600, 72}, {800, 600, 60}, {800, 600, 56},
|
|
{640, 480, 75}, {640, 480, 72}, {640, 480, 67}, {640, 480, 60} };
|
|
pEdid[0x24] = ((((pEdid[0x24] & 0xc0) >> 1) & 0x60) | (pEdid[0x24] & 0x1f)) & 0x7f;
|
|
for (i = 0; i < 13; i++)
|
|
{
|
|
mcArray[i].MinVFreq = MIN_REFRESH_RATE;
|
|
mcArray[i].MinHFreq = 0;
|
|
mcArray[i].MaxHFreq = 0xFFFFFFFF;
|
|
if ((pEdid[0x24-i/7] >> (i%7)) & 0x01)
|
|
numModeCaps = InsertModecapList(&mcArray[i], pModeCaps, numModeCaps);
|
|
}
|
|
|
|
mc.MinVFreq = MIN_REFRESH_RATE;
|
|
mc.MinHFreq = 0;
|
|
mc.MaxHFreq = 0xFFFFFFFF;
|
|
// Standard Timing
|
|
for (i = 0, ptr = &pEdid[0x26]; i < 8; i++, ptr += 2)
|
|
{
|
|
if (*ptr == 0 || *ptr == 1)
|
|
continue;
|
|
|
|
mc.dmWidth = ((ULONG)ptr[0] + 31) << 3;
|
|
c = (ptr[1] >> 6 ) & 0x03;
|
|
mc.dmHeight = mc.dmWidth * r2[c] / r1[c];
|
|
mc.freq = (ULONG)(ptr[1] & 0x3F) + 60;
|
|
|
|
numModeCaps = InsertModecapList(&mc, pModeCaps, numModeCaps);
|
|
}
|
|
|
|
// Detailed timing/Monitor Descriptor
|
|
for (int k = 0; k < 4; k++)
|
|
{
|
|
ptr = &pEdid[0x36+ 18*k];
|
|
if ((ptr[0] != 0 || ptr[1] != 0) && ptr[4] != 0)
|
|
{
|
|
// Detailed timing Descriptor
|
|
mc.dmWidth = (ULONG)ptr[2] + ((ULONG)(ptr[4]&0xF0)) * 0x10;
|
|
mc.dmHeight = (ULONG)ptr[5] + ((ULONG)(ptr[7]&0xF0)) * 0x10;
|
|
if (mc.dmWidth == 0 || mc.dmHeight == 0)
|
|
continue;
|
|
|
|
mc.freq = ((ULONG)ptr[0] + ((ULONG)ptr[1]) * 0x100) * 10000;
|
|
mc.freq /= (mc.dmWidth + (ULONG)ptr[3] + ((ULONG)(ptr[4]&0x0F)) * 0x100) *
|
|
(mc.dmHeight+ (ULONG)ptr[6] + ((ULONG)(ptr[7]&0x0F)) * 0x100);
|
|
// If it's interlaced mode
|
|
if (ptr[17] & 0x80)
|
|
mc.freq >>= 1;
|
|
|
|
numModeCaps = InsertModecapList(&mc, pModeCaps, numModeCaps);
|
|
}
|
|
else if (ptr[3] == 0xFA)
|
|
{
|
|
// Monitor Descriptor--Standard Timing
|
|
for (i = 0, ptr += 5; i < 6; i++, ptr += 2)
|
|
{
|
|
if (*ptr == 0 || *ptr == 1)
|
|
continue;
|
|
|
|
mc.dmWidth = ((ULONG)ptr[0] + 31) << 3;
|
|
c = (ptr[1] >> 6 ) & 0x03;
|
|
mc.dmHeight = mc.dmWidth * r2[c] / r1[c];
|
|
mc.freq = (ULONG)(ptr[1] & 0x3F) + 60;
|
|
|
|
numModeCaps = InsertModecapList(&mc, pModeCaps, numModeCaps);
|
|
}
|
|
}
|
|
else if (ptr[3] == 0xFD)
|
|
{
|
|
pFrequencyRange->ulMinVerticalRate = (ULONG)ptr[5];
|
|
pFrequencyRange->ulMaxVerticalRate = (ULONG)ptr[6];
|
|
pFrequencyRange->ulMinHorizontalRate = ((ULONG)ptr[7]) * 1000;
|
|
pFrequencyRange->ulMaxHorizontalRate = ((ULONG)ptr[8]) * 1000;
|
|
pFrequencyRange->ulMinPixelClock = 0;
|
|
pFrequencyRange->ulMaxPixelClock = ((ULONG)ptr[9]) * 10000000;
|
|
}
|
|
}
|
|
|
|
for (i = (int)numModeCaps-2; i >= 0; i--)
|
|
{
|
|
if (pModeCaps[i].freq < pModeCaps[i+1].freq)
|
|
pModeCaps[i].freq = pModeCaps[i+1].freq;
|
|
}
|
|
|
|
return numModeCaps;
|
|
}
|
|
|
|
ULONG GetMonitorCapability(PDEVICE_OBJECT pdo, LPMODECAP ModeCaps, PFREQUENCY_RANGE pFrequencyRange)
|
|
{
|
|
BYTE pBuffer[512], *pEdid;
|
|
ULONG numModeCaps = 0;
|
|
|
|
//
|
|
// Predefine monitor frequency limits in case we won't get it.
|
|
//
|
|
|
|
GetDefaultFrequencyRange(pFrequencyRange);
|
|
|
|
numModeCaps = GetMonitorCapabilityFromInf(pdo, ModeCaps);
|
|
|
|
if (numModeCaps)
|
|
{
|
|
pFrequencyRange->ulMinVerticalRate = ModeCaps[0].MinVFreq;
|
|
pFrequencyRange->ulMaxVerticalRate = ModeCaps[0].freq;
|
|
return numModeCaps;
|
|
}
|
|
else
|
|
{
|
|
TRACE_INIT(("Drv_Trace: GetMonitorCapabilityFromInf: Failed to retrieve Monitor inf\n"));
|
|
}
|
|
|
|
if (GetRegEDID(pdo, pBuffer, &pEdid) == 0)
|
|
{
|
|
TRACE_INIT(("Drv_Trace: GetMonitorCapability: Failed to get EDID capability\n"));
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Check if EDID Version 2.
|
|
//
|
|
|
|
if (0x20 == pEdid[0])
|
|
{
|
|
return GetMonitorCapability2((PEDID2)pEdid, ModeCaps, pFrequencyRange);
|
|
}
|
|
|
|
//
|
|
// Otherwise always assume it's EDID 1.x
|
|
//
|
|
|
|
return GetMonitorCapability1(pEdid, ModeCaps, pFrequencyRange);
|
|
}
|
|
|
|
BOOL PruneMode(PDEVMODEW pdm, LPMODECAP pModeCaps, int numModeCaps, PFREQUENCY_RANGE pMaxFreqs, ULONG flag)
|
|
{
|
|
ULONG f, f1, freq = pdm->dmDisplayFrequency;
|
|
BOOL bSwapWH = FALSE;
|
|
|
|
if (pdm->dmFields & DM_DISPLAYORIENTATION)
|
|
{
|
|
if (pdm->dmDisplayOrientation == DMDO_90 ||
|
|
pdm->dmDisplayOrientation == DMDO_270)
|
|
{
|
|
bSwapWH = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// For Inf, MaxFreq = {56, 0xFFFFFFFF, 0, 0xFFFFFFFF, 0, 0xFFFFFFFF}
|
|
// The following block only effective to EDID
|
|
//
|
|
if (freq > 1 && (flag & DISPLAY_DEVICE_PRUNE_FREQ)) // Non Driver-default frequency
|
|
{
|
|
if (freq < pMaxFreqs->ulMinVerticalRate)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if (freq > pMaxFreqs->ulMaxVerticalRate && freq > 61)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
f = (LONG)(freq * pdm->dmPelsHeight);
|
|
if (f < pMaxFreqs->ulMinHorizontalRate && freq < 60)
|
|
{
|
|
return TRUE;
|
|
}
|
|
if (f > pMaxFreqs->ulMaxHorizontalRate && freq > 61)
|
|
{
|
|
return TRUE;
|
|
}
|
|
//
|
|
// ISSUE: We should check against MinPixelClock too. Fix in NT 5.1.
|
|
//
|
|
if ((pdm->dmPelsWidth*f) > pMaxFreqs->ulMaxPixelClock)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// 1.05 is a fudge factor that corrects for vetical retrace time.
|
|
// From Win98
|
|
f1 = f * ((pdm->dmPelsHeight > 600) ? 107 : 105) / 100;
|
|
}
|
|
|
|
MODECAP mc = { bSwapWH ? pdm->dmPelsHeight : pdm->dmPelsWidth,
|
|
bSwapWH ? pdm->dmPelsWidth : pdm->dmPelsHeight, freq};
|
|
LPMODECAP pModeCap;
|
|
|
|
// Found weird case of 1200x1600
|
|
if (numModeCaps && (flag & DISPLAY_DEVICE_PRUNE_RESOLUTION))
|
|
{
|
|
if (mc.dmHeight > pModeCaps[numModeCaps-1].dmHeight)
|
|
return TRUE;
|
|
}
|
|
for (int i = 0; i < numModeCaps; i++)
|
|
{
|
|
int comp = compModeCap(&mc, &pModeCaps[i]);
|
|
if (comp > 0)
|
|
{
|
|
if (i >= (numModeCaps-1))
|
|
{
|
|
if (flag & DISPLAY_DEVICE_PRUNE_RESOLUTION) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
continue;
|
|
}
|
|
|
|
if (freq > 1 && (flag & DISPLAY_DEVICE_PRUNE_FREQ)) // Non Driver-default frequency
|
|
{
|
|
pModeCap = (comp == 0 || i == 0) ? &pModeCaps[i] : &pModeCaps[i-1];
|
|
|
|
if (freq > pModeCap->freq && freq > 61 && (comp == 0 || i > 0))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// For EDID, pModeCap = {width, height, freq, 56, 0, 0xFFFFFFFF}.
|
|
// So the following block of code is only effective for INF
|
|
//
|
|
if (freq < pModeCap->MinVFreq)
|
|
{
|
|
return TRUE;
|
|
}
|
|
if (f1 < pModeCap->MinHFreq && freq < 60)
|
|
{
|
|
return TRUE;
|
|
}
|
|
// Sometimes the fulge value downgrades maximum freq below 60, which will lead
|
|
// all modes not supported. Freq 60 has to be supported
|
|
if (f1 > pModeCap->MaxHFreq && freq > 61)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Return Value
|
|
// Number of effective modes
|
|
//
|
|
ULONG PruneModesByMonitors(PGRAPHICS_DEVICE PhysDisp, ULONG numRawModes, PDEVMODEMARK pdevmodeMarks)
|
|
{
|
|
PULONG pNumModeCaps;
|
|
ULONG i, numEffectiveModes;
|
|
PMODECAP pModeCaps = NULL;
|
|
FREQUENCY_RANGE MaxFreqs =
|
|
{
|
|
MIN_REFRESH_RATE, // This is to prune out interlaced modes
|
|
0xffffffff,
|
|
0,
|
|
0xffffffff,
|
|
0,
|
|
0xffffffff
|
|
};
|
|
|
|
PhysDisp->stateFlags &= ~DISPLAY_DEVICE_MODESPRUNED;
|
|
|
|
if (PhysDisp->numMonitorDevice == 0)
|
|
return numRawModes;
|
|
|
|
pNumModeCaps = (PULONG) PALLOCMEM((MAX_MODE_CAPABILITY * sizeof(MODECAP) + sizeof(ULONG)) * PhysDisp->numMonitorDevice,
|
|
GDITAG_DEVMODE);
|
|
if (pNumModeCaps == NULL)
|
|
return numRawModes;
|
|
pModeCaps = (PMODECAP)((PBYTE)pNumModeCaps + sizeof(ULONG) * PhysDisp->numMonitorDevice);
|
|
|
|
TRACE_INIT(("Drv_Trace: PruneModesByMonitors: Enter-- Number of raw modes=%d\n", numRawModes));
|
|
|
|
UpdateMonitorDevices();
|
|
numEffectiveModes = 0;
|
|
for (i = 0; i < PhysDisp->numMonitorDevice; i++)
|
|
{
|
|
pNumModeCaps[i] = 0;
|
|
if (IS_ATTACHED_ACTIVE(PhysDisp->MonitorDevices[i].flag))
|
|
{
|
|
FREQUENCY_RANGE freqs;
|
|
|
|
pNumModeCaps[i] = GetMonitorCapability((PDEVICE_OBJECT)PhysDisp->MonitorDevices[i].pdo, pModeCaps, &freqs);
|
|
if (pNumModeCaps[i] == 0)
|
|
{
|
|
TRACE_INIT(("Drv_Trace: GetMonitorCapability Failed for device %08lx\n", PhysDisp->MonitorDevices[i].pdo));
|
|
}
|
|
|
|
//
|
|
// Intersect frequency ranges for all display devices.
|
|
//
|
|
|
|
MaxFreqs.ulMinVerticalRate = max(MaxFreqs.ulMinVerticalRate, freqs.ulMinVerticalRate);
|
|
MaxFreqs.ulMaxVerticalRate = min(MaxFreqs.ulMaxVerticalRate, freqs.ulMaxVerticalRate);
|
|
MaxFreqs.ulMinHorizontalRate = max(MaxFreqs.ulMinHorizontalRate, freqs.ulMinHorizontalRate);
|
|
MaxFreqs.ulMaxHorizontalRate = min(MaxFreqs.ulMaxHorizontalRate, freqs.ulMaxHorizontalRate);
|
|
MaxFreqs.ulMinPixelClock = max(MaxFreqs.ulMinPixelClock, freqs.ulMinPixelClock);
|
|
MaxFreqs.ulMaxPixelClock = min(MaxFreqs.ulMaxPixelClock, freqs.ulMaxPixelClock);
|
|
}
|
|
pModeCaps += pNumModeCaps[i];
|
|
numEffectiveModes += pNumModeCaps[i];
|
|
}
|
|
|
|
// If get nothing to prune
|
|
if (numEffectiveModes == 0)
|
|
{
|
|
VFREEMEM(pNumModeCaps);
|
|
return numRawModes;
|
|
}
|
|
|
|
numEffectiveModes = 0;
|
|
for ( ; numRawModes > 0; numRawModes--)
|
|
{
|
|
PDEVMODEW pdevMode = pdevmodeMarks[numRawModes-1].pDevMode;
|
|
|
|
TRACE_INIT(("Drv_Trace: PruneMode Width=%d, Height=%d, Freq=%d\n",
|
|
pdevMode->dmPelsWidth, pdevMode->dmPelsHeight, pdevMode->dmDisplayFrequency));
|
|
|
|
pModeCaps = (PMODECAP)((PBYTE)pNumModeCaps + sizeof(ULONG) * PhysDisp->numMonitorDevice);
|
|
for (i = 0; i < PhysDisp->numMonitorDevice; i++)
|
|
{
|
|
if (pNumModeCaps[i] == 0)
|
|
continue;
|
|
if (PruneMode(pdevMode, pModeCaps, pNumModeCaps[i], &MaxFreqs, PhysDisp->MonitorDevices[i].flag))
|
|
{
|
|
TRACE_INIT(("---Pruned\n"));
|
|
pdevmodeMarks[numRawModes-1].bPruned = TRUE;
|
|
PhysDisp->stateFlags |= DISPLAY_DEVICE_MODESPRUNED;
|
|
break;
|
|
}
|
|
pModeCaps += pNumModeCaps[i];
|
|
}
|
|
if (i == PhysDisp->numMonitorDevice)
|
|
numEffectiveModes++;
|
|
}
|
|
|
|
TRACE_INIT(("Drv_Trace: PruneModesByMonitors: Effective Mode Number=%d\n", numEffectiveModes));
|
|
|
|
VFREEMEM(pNumModeCaps);
|
|
|
|
return numEffectiveModes;
|
|
}
|
|
|
|
BOOL CalculateDefaultPreferredModeFromEdid(PDEVICE_OBJECT pdo, LPMODECAP pmc)
|
|
{
|
|
//
|
|
// Try to set our default preferred mode to 800x600x60
|
|
// We try 60Hz only to prevent some crappy EDID from screwing the screen
|
|
// right after Setup, for example, some external LCDs.
|
|
//
|
|
FREQUENCY_RANGE MaxFreqs;
|
|
PMODECAP pModeCaps = (PMODECAP) PALLOCMEM(MAX_MODE_CAPABILITY * sizeof(MODECAP), GDITAG_DEVMODE);
|
|
BOOL bGotMode = FALSE;
|
|
|
|
if (pModeCaps == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
ULONG NumModeCaps = GetMonitorCapability(pdo, pModeCaps, &MaxFreqs);
|
|
|
|
if (NumModeCaps)
|
|
{
|
|
DEVMODEW dm;
|
|
ULONG Freqs[] = {85, 82, 75, 72, 70, 60};
|
|
|
|
pmc->dmWidth = dm.dmPelsWidth = 800 ;
|
|
pmc->dmHeight = dm.dmPelsHeight = 600 ;
|
|
|
|
for (ULONG i = 0; i < sizeof(Freqs)/sizeof(ULONG); i++)
|
|
{
|
|
pmc->freq = dm.dmDisplayFrequency = Freqs[i];
|
|
if (!PruneMode(&dm, pModeCaps, NumModeCaps, &MaxFreqs, DISPLAY_DEVICE_PRUNE_FREQ | DISPLAY_DEVICE_PRUNE_RESOLUTION))
|
|
{
|
|
bGotMode = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
VFREEMEM(pModeCaps);
|
|
|
|
if (!bGotMode) {
|
|
return FALSE;
|
|
}
|
|
|
|
TRACE_INIT(("win32k!GetPreferredModeFromEdid1: Detailed timing %lux%lu at %luHz\n",
|
|
pmc->dmWidth, pmc->dmHeight, pmc->freq));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CalculatePreferredModeFromEdid1 (PBYTE pEdid, PDEVICE_OBJECT pdo, LPMODECAP pmc)
|
|
{
|
|
PBYTE ptr = &pEdid[0x36];
|
|
ULONG count, area;
|
|
|
|
//
|
|
// If EDID has preferrred mode bit
|
|
// This function calculates a preferred mode based on the data in
|
|
// the first detailed timing block. If that data indicates that we can do
|
|
// 800x600x85hz, return that as the preferred mode. Otherwise, just
|
|
// return FALSE indicating that we should fall back to normal default
|
|
// behavior.
|
|
//
|
|
// Otherwise
|
|
// This function returns the preferred mode from the first detailed timing
|
|
// block in the EDID.
|
|
//
|
|
|
|
for (count = 0; count < 4; count++) {
|
|
ptr += count * 18;
|
|
if ((ptr[0] != 0 || ptr[1] != 0) && ptr[4] != 0) {
|
|
pmc->dmWidth = (ULONG) ptr[2] + ((ULONG)(ptr[4] & 0xF0)) * 0x10;
|
|
pmc->dmHeight = (ULONG) ptr[5] + ((ULONG)(ptr[7] & 0xF0)) * 0x10 ;
|
|
pmc->freq = ((ULONG)ptr[0] + ((ULONG)ptr[1]) * 0x100) * 10000;
|
|
area = (pmc->dmWidth + (ULONG)ptr[3] + ((ULONG)(ptr[4]&0x0F)) * 0x100) *
|
|
(pmc->dmHeight+ (ULONG)ptr[6] + ((ULONG)(ptr[7]&0x0F)) * 0x100);
|
|
if (area == 0) {
|
|
ASSERT (FALSE);
|
|
return FALSE;
|
|
}
|
|
pmc->freq /= area;
|
|
if ((pEdid[0x18] & 0x02)) {
|
|
TRACE_INIT(("win32k!GetPreferredModeFromEdid1: Detailed timing %lux%lu at %luHz\n",
|
|
pmc->dmWidth, pmc->dmHeight, pmc->freq));
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return CalculateDefaultPreferredModeFromEdid(pdo, pmc);
|
|
}
|
|
|
|
|
|
BOOL CalculatePreferredModeFromEdid2 (PEDID2 pEdid2, PDEVICE_OBJECT pdo, LPMODECAP pmc)
|
|
{
|
|
ULONG ulEdidOffset = EDID2_LUMINANCE_TABLE_OFFSET,
|
|
count;
|
|
|
|
//
|
|
// No detailed timing block
|
|
//
|
|
if ((pEdid2->ucaMapOfTiming[1] & EDID2_MOT1_DETAIL_TIMING_MASK) == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
PUCHAR pucLuminanceTable = (PUCHAR)pEdid2 + ulEdidOffset;
|
|
|
|
//
|
|
// Luminance Table
|
|
//
|
|
ulEdidOffset += (((*pucLuminanceTable & EDID2_LT0_SUB_CHANNELS_FLAG) ? 3 : 1) *
|
|
(*pucLuminanceTable & EDID2_LT0_ENTRIES_MASK) + 1);
|
|
|
|
//
|
|
// Frequency Ranges
|
|
//
|
|
ulEdidOffset += ((pEdid2->ucaMapOfTiming[0] & EDID2_MOT0_FREQUENCY_RANGE_MASK) >>
|
|
EDID2_MOT0_FREQUENCY_RANGE_SHIFT) *
|
|
sizeof (EDID2_FREQUENCY_RANGE);
|
|
|
|
//
|
|
// Detailed Range Limits
|
|
//
|
|
ulEdidOffset += ((pEdid2->ucaMapOfTiming[0] & EDID2_MOT0_DETAIL_TIMING_RANGE_MASK) >>
|
|
EDID2_MOT0_DETAIL_TIMING_RANGE_SHIFT) *
|
|
sizeof(EDID2_DETAIL_TIMING_RANGE);
|
|
|
|
//
|
|
// Timing Codes
|
|
//
|
|
ulEdidOffset += ((pEdid2->ucaMapOfTiming[1] & EDID2_MOT1_TIMING_CODE_MASK) >>
|
|
EDID2_MOT1_TIMING_CODE_SHIFT) *
|
|
sizeof(EDID2_TIMING_CODE);
|
|
|
|
ULONG ulNumberOfDetailTimings = (pEdid2->ucaMapOfTiming[1] & EDID2_MOT1_DETAIL_TIMING_MASK) >>
|
|
EDID2_MOT1_DETAIL_TIMING_SHIFT;
|
|
for (count = 0; count < ulNumberOfDetailTimings; count++)
|
|
{
|
|
ulEdidOffset += count*sizeof(EDID2_DETAIL_TIMING);
|
|
if ((ulEdidOffset + sizeof(EDID2_DETAIL_TIMING)) >= sizeof (EDID2))
|
|
{
|
|
ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
PEDID2_DETAIL_TIMING pDetailTiming = (PEDID2_DETAIL_TIMING)((PUCHAR)pEdid2 + ulEdidOffset);
|
|
|
|
pmc->dmWidth = ((ULONG)(pDetailTiming->ucHorizontalHighBits & 0xf0) << 4) +
|
|
pDetailTiming->ucHorizontalActiveLowByte;
|
|
|
|
pmc->dmHeight = ((ULONG)(pDetailTiming->ucVerticalHighBits & 0xf0) << 4) +
|
|
pDetailTiming->ucVerticalActiveLowByte;
|
|
|
|
ULONG ulHorizontalTotal = ((ULONG)(pDetailTiming->ucHorizontalHighBits & 0x0f) << 8) +
|
|
pDetailTiming->ucHorizontalBlankLowByte + pmc->dmWidth;
|
|
|
|
ULONG ulVerticalTotal = ((ULONG)(pDetailTiming->ucVerticalHighBits & 0x0f) << 8) +
|
|
pDetailTiming->ucVerticalBlankLowByte + pmc->dmHeight;
|
|
|
|
//
|
|
// Make sure we won't divide by zero in case of bad EDID.
|
|
//
|
|
if ((0 == ulHorizontalTotal) || (0 == ulVerticalTotal))
|
|
{
|
|
ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Calculate refresh rate rounding to the nearest integer.
|
|
//
|
|
|
|
pmc->freq = ((((ULONG)(pDetailTiming->usPixelClock) * 100000) / (ulHorizontalTotal * ulVerticalTotal)) + 5) / 10;
|
|
|
|
if (pDetailTiming->ucFlags & EDID2_DT_INTERLACED) {
|
|
pmc->freq /= 2;
|
|
}
|
|
|
|
if (pEdid2->ucaMapOfTiming[0] & EDID2_MOT0_PREFFERED_MODE_FLAG)
|
|
{
|
|
TRACE_INIT(("win32k!GetPreferredModeFromEdid2: Detailed timing %lux%lu at %luHz\n",
|
|
pmc->dmWidth, pmc->dmHeight, pmc->freq));
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return CalculateDefaultPreferredModeFromEdid(pdo, pmc);
|
|
}
|
|
|
|
BOOL GetPreferredModeFromEdid(PDEVICE_OBJECT pdo, LPMODECAP pmc)
|
|
{
|
|
BYTE pBuffer[512], *pEdid;
|
|
if (!GetRegEDID(pdo, pBuffer, &pEdid))
|
|
return FALSE;
|
|
|
|
//
|
|
// It's EDID2
|
|
//
|
|
if (0x20 == pEdid[0]) {
|
|
return CalculatePreferredModeFromEdid2((PEDID2)pEdid, pdo, pmc);
|
|
}
|
|
|
|
if (pEdid[0] != 0 || pEdid[7] != 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
for (int i = 1; i < 7; i++)
|
|
{
|
|
if (pEdid[i] != 0xFF)
|
|
return FALSE;
|
|
}
|
|
|
|
return (CalculatePreferredModeFromEdid1 (pEdid, pdo, pmc));
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrvGetPreferredMode
|
|
*
|
|
* Routine that returns the prefered mode.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
NTSTATUS
|
|
DrvGetPreferredMode(
|
|
LPDEVMODEW lpDevMode,
|
|
PGRAPHICS_DEVICE PhysDisp
|
|
)
|
|
{
|
|
NTSTATUS retval = STATUS_INVALID_PARAMETER_1;
|
|
DWORD iModeNum;
|
|
|
|
ASSERTGDI((lpDevMode != NULL), "Invalid lpDevMode\n");
|
|
ASSERTGDI((PhysDisp != NULL), "Invalid PhysDisp\n");
|
|
|
|
UpdateMonitorDevices();
|
|
|
|
__try
|
|
{
|
|
lpDevMode->dmPelsWidth = lpDevMode->dmPelsHeight = lpDevMode->dmDisplayFrequency = 0x7FFF;
|
|
|
|
for (iModeNum = 0; iModeNum < PhysDisp->numMonitorDevice; iModeNum++)
|
|
{
|
|
if (IS_ATTACHED_ACTIVE(PhysDisp->MonitorDevices[iModeNum].flag))
|
|
{
|
|
MODECAP mc;
|
|
if (!GetValuesFromInf((PDEVICE_OBJECT)PhysDisp->MonitorDevices[iModeNum].pdo, 0, &mc))
|
|
{
|
|
if (!GetPreferredModeFromEdid((PDEVICE_OBJECT)PhysDisp->MonitorDevices[iModeNum].pdo, &mc))
|
|
{
|
|
lpDevMode->dmDisplayFrequency = 60;
|
|
continue;
|
|
}
|
|
}
|
|
//
|
|
// To be safe, always pick the smaller refresh rate
|
|
//
|
|
if (mc.freq < lpDevMode->dmDisplayFrequency)
|
|
lpDevMode->dmDisplayFrequency = mc.freq;
|
|
|
|
if (mc.dmWidth > lpDevMode->dmPelsWidth)
|
|
continue;
|
|
if (mc.dmWidth == lpDevMode->dmPelsWidth &&
|
|
mc.dmHeight > lpDevMode->dmPelsHeight)
|
|
continue;
|
|
|
|
lpDevMode->dmPelsWidth = mc.dmWidth;
|
|
lpDevMode->dmPelsHeight = mc.dmHeight;
|
|
lpDevMode->dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
|
|
}
|
|
|
|
}
|
|
|
|
if (lpDevMode->dmPelsWidth == 0x7FFF)
|
|
retval = STATUS_INVALID_PARAMETER_3;
|
|
else
|
|
retval = STATUS_SUCCESS;
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrvBuildDevmodeList
|
|
*
|
|
* Builds the list of DEVMODEs for a particular GRAPHICS_DEVICE structure
|
|
*
|
|
* CRIT must be held before this call is made.
|
|
*
|
|
* History:
|
|
* 10-Mar-1996 andreva Created.
|
|
\***************************************************************************/
|
|
|
|
VOID
|
|
DrvBuildDevmodeList(
|
|
PGRAPHICS_DEVICE PhysDisp,
|
|
BOOL bUpdateCache)
|
|
{
|
|
GDIFunctionID(DrvBuildDevmodeList);
|
|
|
|
ULONG i, j;
|
|
PDRV_NAMES lpNames = NULL;
|
|
DWORD cbOutputSize;
|
|
LPDEVMODEW tmpBuffer;
|
|
PUCHAR reallocBuffer;
|
|
|
|
|
|
//
|
|
// check if the information is cached already
|
|
// if not, then get the information from the drivers.
|
|
//
|
|
// NOTE : we may want to synchronize access to this list
|
|
// of modes so that we can dynamically update the list
|
|
// when plug - and - play arrives.
|
|
//
|
|
// NOTE : the list of text modes is built at boot time, and we depend
|
|
// on that list being valid if the PhysDisp is returned.
|
|
// see DrvInitConsole().
|
|
//
|
|
|
|
TRACE_INIT(("Drv_Trace: BuildDevmode: Enter"));
|
|
|
|
// If HYDRA Remote session we don't want to cache the mode as
|
|
// we can be in a reconnect with a different resolution.
|
|
// It these case the driver's only mode has changed so
|
|
// we want to query him to get latest valid mode
|
|
//
|
|
// bUpdateCache = TRUE only when display switching occured. At this time,
|
|
// we need to update mode list
|
|
//
|
|
|
|
if ((PhysDisp->stateFlags & DISPLAY_DEVICE_REMOTE) || bUpdateCache)
|
|
{
|
|
if ( (PhysDisp != &gFullscreenGraphicsDevice) &&
|
|
( (PhysDisp->cbdevmodeInfo != 0) && (PhysDisp->devmodeInfo != NULL)) )
|
|
{
|
|
if (PhysDisp->devmodeInfo)
|
|
{
|
|
VFREEMEM(PhysDisp->devmodeInfo);
|
|
PhysDisp->devmodeInfo = NULL;
|
|
}
|
|
PhysDisp->cbdevmodeInfo = 0;
|
|
if (PhysDisp->devmodeMarks)
|
|
{
|
|
VFREEMEM(PhysDisp->devmodeMarks);
|
|
PhysDisp->devmodeMarks = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( (PhysDisp != &gFullscreenGraphicsDevice) &&
|
|
(PhysDisp->cbdevmodeInfo == 0) &&
|
|
(PhysDisp->devmodeInfo == NULL) )
|
|
{
|
|
TRACE_INIT(("\nDrv_Trace: BuildDevmode: Rebuild List\n"));
|
|
|
|
PhysDisp->numRawModes = 0;
|
|
|
|
lpNames = DrvGetDisplayDriverNames(PhysDisp);
|
|
|
|
if (lpNames)
|
|
{
|
|
for (i = 0; i < lpNames->cNames; i++)
|
|
{
|
|
|
|
cbOutputSize = ldevGetDriverModes(lpNames->D[i].lpDisplayName,
|
|
lpNames->D[i].hDriver,
|
|
&tmpBuffer);
|
|
|
|
if (cbOutputSize)
|
|
{
|
|
//
|
|
// create a new buffer copy the old data into it
|
|
// and append the new data at the end - we want
|
|
// a continuous buffer for all the data.
|
|
//
|
|
|
|
reallocBuffer = (PUCHAR) PALLOCNOZ(PhysDisp->cbdevmodeInfo + cbOutputSize,
|
|
GDITAG_DRVSUP);
|
|
|
|
if (reallocBuffer)
|
|
{
|
|
if (PhysDisp->cbdevmodeInfo)
|
|
{
|
|
//
|
|
// Copy the contents of the old buffer
|
|
// and free it
|
|
//
|
|
|
|
RtlCopyMemory(reallocBuffer,
|
|
PhysDisp->devmodeInfo,
|
|
PhysDisp->cbdevmodeInfo);
|
|
|
|
VFREEMEM(PhysDisp->devmodeInfo);
|
|
}
|
|
|
|
RtlCopyMemory(reallocBuffer +
|
|
PhysDisp->cbdevmodeInfo,
|
|
tmpBuffer,
|
|
cbOutputSize);
|
|
|
|
PhysDisp->cbdevmodeInfo += cbOutputSize;
|
|
PhysDisp->devmodeInfo = (PDEVMODEW) reallocBuffer;
|
|
}
|
|
else
|
|
{
|
|
WARNING("failed realloc\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WARNING("display driver not present\n");
|
|
}
|
|
|
|
if (tmpBuffer) {
|
|
VFREEMEM(tmpBuffer);
|
|
}
|
|
}
|
|
|
|
VFREEMEM(lpNames);
|
|
}
|
|
|
|
if ( (PhysDisp->cbdevmodeInfo == 0) &&
|
|
(PhysDisp->devmodeInfo == NULL) )
|
|
{
|
|
DrvLogDisplayDriverEvent(MsgInvalidDisplayDriver);
|
|
}
|
|
else
|
|
{
|
|
PDEVMODEW lpdm, lpdm1;
|
|
for (i = 0, cbOutputSize = 0; cbOutputSize < PhysDisp->cbdevmodeInfo; i++)
|
|
{
|
|
lpdm = (LPDEVMODEW)(((LPBYTE)PhysDisp->devmodeInfo) + cbOutputSize);
|
|
cbOutputSize += lpdm->dmSize + lpdm->dmDriverExtra;
|
|
}
|
|
|
|
PhysDisp->devmodeMarks = (PDEVMODEMARK)PALLOCMEM(i * sizeof(DEVMODEMARK),
|
|
GDITAG_DRVSUP);
|
|
if (PhysDisp->devmodeMarks == NULL)
|
|
{
|
|
PhysDisp->cbdevmodeInfo = 0;
|
|
VFREEMEM(PhysDisp->devmodeInfo);
|
|
PhysDisp->devmodeInfo = NULL;
|
|
WARNING("failed allocate lookside list\n");
|
|
DrvLogDisplayDriverEvent(MsgInvalidDisplayDriver);
|
|
}
|
|
else
|
|
{
|
|
PhysDisp->numRawModes = i;
|
|
for (i = 0, cbOutputSize = 0; cbOutputSize < PhysDisp->cbdevmodeInfo; i++)
|
|
{
|
|
lpdm = (LPDEVMODEW)(((LPBYTE)PhysDisp->devmodeInfo) + cbOutputSize);
|
|
|
|
// jasonha 07/13/2001 Display Orientation Support
|
|
if (!(lpdm->dmFields & DM_DISPLAYORIENTATION))
|
|
{
|
|
lpdm->dmFields |= DM_DISPLAYORIENTATION;
|
|
if (lpdm->dmDisplayOrientation != 0)
|
|
{
|
|
WARNING("driver reported non-zero dmDisplayOrientation, but not DM_DISPLAYORIENTATION flag.\n");
|
|
}
|
|
lpdm->dmDisplayOrientation = DMDO_DEFAULT;
|
|
}
|
|
else if (lpdm->dmDisplayOrientation > DMDO_LAST)
|
|
{
|
|
WARNING("driver reported invalid dmDisplayOrientation.\n");
|
|
lpdm->dmDisplayOrientation = DMDO_DEFAULT;
|
|
}
|
|
|
|
// jasonha 09/17/2001 Display Fixed Output Support
|
|
if (!(lpdm->dmFields & DM_DISPLAYFIXEDOUTPUT))
|
|
{
|
|
if (lpdm->dmDisplayFixedOutput != 0)
|
|
{
|
|
WARNING("driver reported non-zero dmDisplayFixedOutput, but not DM_DISPLAYFIXEDOUTPUT flag.\n");
|
|
}
|
|
lpdm->dmDisplayFixedOutput = DMDFO_DEFAULT;
|
|
}
|
|
else if (lpdm->dmDisplayFixedOutput == DMDFO_DEFAULT ||
|
|
lpdm->dmDisplayFixedOutput > DMDFO_LAST)
|
|
{
|
|
WARNING("driver reported invalid dmDisplayFixedOutput.\n");
|
|
lpdm->dmFields &= ~DM_DISPLAYFIXEDOUTPUT;
|
|
lpdm->dmDisplayFixedOutput = DMDFO_DEFAULT;
|
|
}
|
|
|
|
PhysDisp->devmodeMarks[i].bPruned = FALSE;
|
|
PhysDisp->devmodeMarks[i].pDevMode = lpdm;
|
|
cbOutputSize += lpdm->dmSize + lpdm->dmDriverExtra;
|
|
}
|
|
|
|
// 11/26/98 Ignore miniport default refresh rate
|
|
// But if the default rate is the only mode in the list, we still keep it
|
|
// For example, VGA and mnmdd
|
|
for (i = 1; i <= PhysDisp->numRawModes; i++)
|
|
{
|
|
lpdm = PhysDisp->devmodeMarks[i-1].pDevMode;
|
|
if (lpdm->dmDisplayFrequency == 1)
|
|
{
|
|
for (j = 1; j <= PhysDisp->numRawModes; j++)
|
|
{
|
|
if (j == i)
|
|
continue;
|
|
lpdm1 = PhysDisp->devmodeMarks[j-1].pDevMode;
|
|
if (lpdm->dmBitsPerPel != lpdm1->dmBitsPerPel)
|
|
continue;
|
|
if (lpdm->dmPelsWidth != lpdm1->dmPelsWidth)
|
|
continue;
|
|
if (lpdm->dmPelsHeight != lpdm1->dmPelsHeight)
|
|
continue;
|
|
if ((lpdm->dmDisplayFlags & DMDISPLAYFLAGS_TEXTMODE) !=
|
|
(lpdm1->dmDisplayFlags & DMDISPLAYFLAGS_TEXTMODE))
|
|
continue;
|
|
if (lpdm->dmDisplayOrientation != lpdm1->dmDisplayOrientation)
|
|
continue;
|
|
if (lpdm->dmDisplayFixedOutput != lpdm1->dmDisplayFixedOutput)
|
|
continue;
|
|
// We find a duplicated mode, so cut off this mode
|
|
if (PhysDisp->numRawModes > i)
|
|
RtlMoveMemory(&PhysDisp->devmodeMarks[i-1],
|
|
&PhysDisp->devmodeMarks[i],
|
|
sizeof(DEVMODEMARK) * (PhysDisp->numRawModes-i));
|
|
PhysDisp->numRawModes--;
|
|
i--;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((PhysDisp->stateFlags & DISPLAY_DEVICE_REMOTE) == 0 &&
|
|
(PhysDisp->stateFlags & DISPLAY_DEVICE_DISCONNECT) == 0 &&
|
|
(PhysDisp->stateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) == 0)
|
|
cbOutputSize = PruneModesByMonitors(PhysDisp, PhysDisp->numRawModes, PhysDisp->devmodeMarks);
|
|
|
|
if (cbOutputSize == 0)
|
|
DrvLogDisplayDriverEvent(MsgInvalidDisplayDriver);
|
|
|
|
//
|
|
// Expand all 32bpp modes to double the effective dots-per-inch.
|
|
// Panning.cxx handles the actual downsizing.
|
|
//
|
|
|
|
if (G_fDoubleDpi)
|
|
{
|
|
for (i = 0, cbOutputSize = 0; cbOutputSize < PhysDisp->cbdevmodeInfo; i++)
|
|
{
|
|
lpdm = (LPDEVMODEW)(((LPBYTE)PhysDisp->devmodeInfo) + cbOutputSize);
|
|
if (lpdm->dmBitsPerPel == 32)
|
|
{
|
|
lpdm->dmPelsWidth *= 2;
|
|
lpdm->dmPelsHeight *= 2;
|
|
}
|
|
cbOutputSize += lpdm->dmSize + lpdm->dmDriverExtra;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRACE_INIT((" - Use cache"));
|
|
}
|
|
|
|
TRACE_INIT((" - Exit\n"));
|
|
|
|
return;
|
|
}
|
|
|
|
#define DIFF(x, y) ( ((x) >= (y)) ? ((x)-(y)) : ((y)-(x)) )
|
|
|
|
// Orientation Difference Table
|
|
// Table lookup returns matching level for two dmDisplayOrientation values.
|
|
// Lower number mean a better match.
|
|
// Exact match => 0
|
|
// 180 rotation => 1
|
|
// 90/270 rotation => 2
|
|
|
|
DWORD dwOrientationDiffTable[4][4] = {
|
|
{ 0, 2, 1, 2},
|
|
{ 2, 0, 2, 1},
|
|
{ 1, 2, 0, 2},
|
|
{ 2, 1, 2, 0}
|
|
};
|
|
|
|
#define ORIENTATION_DIFF(x, y) dwOrientationDiffTable[y][x]
|
|
|
|
LPDEVMODEW GetClosestMode(PGRAPHICS_DEVICE PhysDisp, LPDEVMODEW pOrgDevMode, BOOL bPrune)
|
|
{
|
|
DEVMODEW diffMode;
|
|
LPDEVMODEW lpdm, lpModePicked = NULL, lpModeWanted = NULL;
|
|
ULONG diff;
|
|
|
|
diffMode.dmBitsPerPel = 0xFFFFFFFF;
|
|
diffMode.dmPelsWidth = 0xFFFFFFFF;
|
|
diffMode.dmPelsHeight = 0xFFFFFFFF;
|
|
diffMode.dmDisplayFrequency = 0xFFFFFFFF;
|
|
diffMode.dmDisplayOrientation = 0xFFFFFFFF;
|
|
diffMode.dmDisplayFixedOutput = 0xFFFFFFFF;
|
|
|
|
if (pOrgDevMode->dmDisplayFrequency == 0)
|
|
pOrgDevMode->dmDisplayFrequency = 60;
|
|
if (pOrgDevMode->dmBitsPerPel == 0)
|
|
pOrgDevMode->dmBitsPerPel = 32;
|
|
if ( !(pOrgDevMode->dmFields & DM_DISPLAYORIENTATION) )
|
|
pOrgDevMode->dmDisplayOrientation = DMDO_DEFAULT;
|
|
if ( !(pOrgDevMode->dmFields & DM_DISPLAYFIXEDOUTPUT) )
|
|
pOrgDevMode->dmDisplayFixedOutput = DMDFO_DEFAULT;
|
|
|
|
|
|
//
|
|
// Do 2 rounds of search.
|
|
// First round, only find smaller or exact mode. Done if exact match.
|
|
// Second round is used done if:
|
|
// Orientation doesn't match or
|
|
// Mode is smalller than 640x480x60 (480x640x60 for rotated modes)
|
|
//
|
|
for (ULONG k = 0; k < 2; k++)
|
|
{
|
|
if (lpModeWanted &&
|
|
diffMode.dmDisplayOrientation == 0 &&
|
|
((lpModeWanted->dmPelsWidth >= lpModeWanted->dmPelsHeight) ?
|
|
(lpModeWanted->dmPelsWidth >= 640 &&
|
|
lpModeWanted->dmPelsHeight>= 480) :
|
|
(lpModeWanted->dmPelsWidth >= 480 &&
|
|
lpModeWanted->dmPelsHeight>= 640)) &&
|
|
lpModeWanted->dmDisplayFrequency >= 60)
|
|
{
|
|
break;
|
|
}
|
|
|
|
for (ULONG i = 0; i < PhysDisp->numRawModes; i++)
|
|
{
|
|
if (bPrune && PhysDisp->devmodeMarks[i].bPruned)
|
|
continue;
|
|
lpdm = PhysDisp->devmodeMarks[i].pDevMode;
|
|
|
|
if (pOrgDevMode->dmFields & DM_DISPLAYORIENTATION)
|
|
{
|
|
diff = ORIENTATION_DIFF(pOrgDevMode->dmDisplayOrientation, lpdm->dmDisplayOrientation);
|
|
|
|
if (diffMode.dmDisplayOrientation < diff) continue;
|
|
|
|
if (diffMode.dmDisplayOrientation > diff)
|
|
{
|
|
lpModePicked = lpdm;
|
|
}
|
|
}
|
|
|
|
if (pOrgDevMode->dmPelsWidth && lpModePicked != lpdm)
|
|
{
|
|
diff = DIFF(pOrgDevMode->dmPelsWidth, lpdm->dmPelsWidth);
|
|
if (diffMode.dmPelsWidth < diff)
|
|
continue;
|
|
else if (diffMode.dmPelsWidth > diff)
|
|
{
|
|
lpModePicked = lpdm;
|
|
}
|
|
}
|
|
if (pOrgDevMode->dmPelsHeight && lpModePicked != lpdm)
|
|
{
|
|
diff = DIFF(pOrgDevMode->dmPelsHeight, lpdm->dmPelsHeight);
|
|
if (diffMode.dmPelsHeight < diff)
|
|
continue;
|
|
else if (diffMode.dmPelsHeight > diff)
|
|
{
|
|
lpModePicked = lpdm;
|
|
}
|
|
}
|
|
if (lpModePicked != lpdm)
|
|
{
|
|
diff = DIFF(pOrgDevMode->dmBitsPerPel, lpdm->dmBitsPerPel);
|
|
if (diffMode.dmBitsPerPel < diff)
|
|
continue;
|
|
else if (diffMode.dmBitsPerPel > diff)
|
|
{
|
|
lpModePicked = lpdm;
|
|
}
|
|
}
|
|
if (lpModePicked != lpdm)
|
|
{
|
|
diff = (pOrgDevMode->dmDisplayFixedOutput != lpdm->dmDisplayFixedOutput) ? 1 : 0;
|
|
if (diffMode.dmDisplayFixedOutput < diff)
|
|
continue;
|
|
else if (diffMode.dmDisplayFixedOutput > diff)
|
|
{
|
|
lpModePicked = lpdm;
|
|
}
|
|
}
|
|
if (lpModePicked != lpdm)
|
|
{
|
|
diff = DIFF(pOrgDevMode->dmDisplayFrequency, lpdm->dmDisplayFrequency);
|
|
if (diffMode.dmDisplayFrequency < diff)
|
|
continue;
|
|
else if (diffMode.dmDisplayFrequency > diff)
|
|
{
|
|
lpModePicked = lpdm;
|
|
}
|
|
}
|
|
|
|
// continue if we haven't found a better match
|
|
if (lpModePicked != lpdm)
|
|
continue;
|
|
|
|
if (k == 0)
|
|
{
|
|
// skip larger or higher frequency modes (first round only)
|
|
if (lpModePicked->dmPelsWidth > pOrgDevMode->dmPelsWidth &&
|
|
pOrgDevMode->dmPelsWidth)
|
|
continue;
|
|
if (lpModePicked->dmPelsHeight > pOrgDevMode->dmPelsHeight &&
|
|
pOrgDevMode->dmPelsHeight != 0)
|
|
continue;
|
|
if (lpModePicked->dmDisplayFrequency > pOrgDevMode->dmDisplayFrequency)
|
|
continue;
|
|
}
|
|
|
|
lpModeWanted = lpModePicked;
|
|
diffMode.dmDisplayOrientation = ORIENTATION_DIFF(pOrgDevMode->dmDisplayOrientation, lpdm->dmDisplayOrientation);
|
|
diffMode.dmPelsWidth = DIFF(pOrgDevMode->dmPelsWidth, lpdm->dmPelsWidth);
|
|
diffMode.dmPelsHeight = DIFF(pOrgDevMode->dmPelsHeight, lpdm->dmPelsHeight);
|
|
diffMode.dmBitsPerPel = DIFF(pOrgDevMode->dmBitsPerPel, lpdm->dmBitsPerPel);
|
|
diffMode.dmDisplayFixedOutput = (pOrgDevMode->dmDisplayFixedOutput != lpdm->dmDisplayFixedOutput) ? 1 : 0;
|
|
diffMode.dmDisplayFrequency = DIFF(pOrgDevMode->dmDisplayFrequency, lpdm->dmDisplayFrequency);
|
|
|
|
if (diffMode.dmDisplayOrientation == 0 &&
|
|
diffMode.dmBitsPerPel == 0 &&
|
|
diffMode.dmPelsWidth == 0 &&
|
|
diffMode.dmPelsHeight == 0 &&
|
|
diffMode.dmDisplayFixedOutput == 0 &&
|
|
diffMode.dmDisplayFrequency == 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
if (lpModeWanted == NULL)
|
|
{
|
|
WARNING("Drv_Trace: GetClosestMode: The PhysDisp driver has Zero mode\n");
|
|
}
|
|
#endif
|
|
|
|
return lpModeWanted;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* ProbeAndCaptureDevmode
|
|
*
|
|
* Maps a partial DEVMODE (for example, may only contain width and height)
|
|
* to a complete DEVMODE that the kernel routines will like.
|
|
*
|
|
* CRIT need not be held when calling.
|
|
*
|
|
* History:
|
|
* 10-Mar-1996 andreva Created.
|
|
\***************************************************************************/
|
|
|
|
NTSTATUS
|
|
DrvProbeAndCaptureDevmode(
|
|
PGRAPHICS_DEVICE PhysDisp,
|
|
PDEVMODEW *DestinationDevmode,
|
|
BOOL *bDetach,
|
|
PDEVMODEW SourceDevmode,
|
|
BOOL bDefaultMode,
|
|
MODE PreviousMode,
|
|
BOOL bPrune,
|
|
BOOL bClosest,
|
|
BOOL bFromMonitor
|
|
)
|
|
{
|
|
NTSTATUS ntRet = STATUS_UNSUCCESSFUL;
|
|
BOOL bRet = FALSE;
|
|
BOOL btmpError;
|
|
ULONG sourceSize;
|
|
ULONG sourceSizeExtra;
|
|
ULONG sizeExtra;
|
|
PDEVMODEW matchedDevmode = NULL;
|
|
PDEVMODEW partialDevmode;
|
|
|
|
DWORD tmpDisplayFlags = 0;
|
|
DWORD tmpPanningWidth = 0;
|
|
DWORD tmpPanningHeight = 0;
|
|
DWORD tmpPositionX = 0;
|
|
DWORD tmpPositionY = 0;
|
|
BOOL tmpPosition;
|
|
BOOL bOrientationSpecified = FALSE;
|
|
BOOL bFixedOutputSpecified = FALSE;
|
|
|
|
|
|
TRACE_INIT(("Drv_Trace: CaptMatchDevmode: Entering\n"));
|
|
|
|
*DestinationDevmode = NULL;
|
|
*bDetach = FALSE;
|
|
|
|
|
|
if (SourceDevmode == NULL)
|
|
{
|
|
TRACE_INIT(("Drv_Trace: CaptMatchDevmode: Exit DEVMODE NULL\n\n"));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
TRACE_INIT(("\n"));
|
|
TRACE_INIT((" pDevMode %08lx\n", SourceDevmode));
|
|
|
|
partialDevmode = (PDEVMODEW) PALLOCNOZ(sizeof(DEVMODEW) + MAXUSHORT,
|
|
GDITAG_DEVMODE);
|
|
|
|
if (partialDevmode == NULL)
|
|
{
|
|
TRACE_INIT(("Drv_Trace: CaptMatchDevmode: Could not allocate partial DEVMODE\n\n"));
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Put everything in a try except so we can always reference the original
|
|
// passed in structure.
|
|
//
|
|
|
|
__try
|
|
{
|
|
if (PreviousMode == UserMode)
|
|
{
|
|
ProbeForRead(SourceDevmode,
|
|
FIELD_OFFSET(DEVMODEW, dmFields),
|
|
sizeof(DWORD));
|
|
}
|
|
else
|
|
{
|
|
ASSERTGDI(SourceDevmode >= (PDEVMODEW const)MM_USER_PROBE_ADDRESS,
|
|
"Bad kernel mode address\n");
|
|
}
|
|
|
|
//
|
|
// Capture these so that they don't change right after the probe.
|
|
//
|
|
|
|
sourceSize = SourceDevmode->dmSize;
|
|
sourceSizeExtra = SourceDevmode->dmDriverExtra;
|
|
|
|
if (PreviousMode == UserMode)
|
|
{
|
|
ProbeForRead(SourceDevmode,
|
|
sourceSize + sourceSizeExtra,
|
|
sizeof(DWORD));
|
|
}
|
|
|
|
dbgDumpDevmode(SourceDevmode);
|
|
|
|
if (SourceDevmode->dmFields == 0)
|
|
bClosest = TRUE;
|
|
|
|
//
|
|
// At the introduction time of this API, the DEVMODE already contained
|
|
// up to the dmDisplayFrequency field. We will fail is the DEVMODE is
|
|
// smaller than that.
|
|
//
|
|
|
|
if (sourceSize >= FIELD_OFFSET(DEVMODEW, dmICMMethod))
|
|
{
|
|
//
|
|
// Determine if the position reflects a detach operation
|
|
//
|
|
// If the size of the rectangle is NULL, that means the device
|
|
// needs to be detached from the desktop.
|
|
//
|
|
|
|
if ((SourceDevmode->dmFields & DM_POSITION) &&
|
|
(SourceDevmode->dmFields & DM_PELSWIDTH) &&
|
|
(SourceDevmode->dmPelsWidth == 0) &&
|
|
(SourceDevmode->dmFields & DM_PELSHEIGHT) &&
|
|
(SourceDevmode->dmPelsHeight == 0))
|
|
{
|
|
*bDetach = TRUE;
|
|
VFREEMEM(partialDevmode);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Lets build a temporary DEVMODE that will contain the
|
|
// "wished for" DEVMODE, based on matching from the registry.
|
|
// Only match the basic devmode. Other fields (optional ones
|
|
// will be added later)
|
|
//
|
|
// NOTE special case VGA mode so that we don't try to match to the
|
|
// current screen mode.
|
|
//
|
|
|
|
RtlZeroMemory(partialDevmode, sizeof(DEVMODEW));
|
|
partialDevmode->dmSize = 0xDDDD;
|
|
partialDevmode->dmDriverExtra = MAXUSHORT;
|
|
|
|
if (PhysDisp == &gFullscreenGraphicsDevice)
|
|
{
|
|
//
|
|
// We should never get called to probe the fullscreen modes
|
|
// since they are only called by the console
|
|
//
|
|
|
|
ASSERTGDI(FALSE, "ProbeAndCaptureDEVMODE called with VGA device\n");
|
|
VFREEMEM(partialDevmode);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
else if (bDefaultMode)
|
|
{
|
|
//
|
|
// We just want to pick the default mode from the driver.
|
|
//
|
|
// Leave the partial DEVMODE with all NULLs
|
|
//
|
|
|
|
DrvGetDisplayDriverParameters(PhysDisp,
|
|
partialDevmode,
|
|
TRUE,
|
|
bFromMonitor);
|
|
}
|
|
else
|
|
{
|
|
if (!NT_SUCCESS(DrvGetDisplayDriverParameters(PhysDisp,
|
|
partialDevmode,
|
|
gbBaseVideo,
|
|
bFromMonitor)))
|
|
{
|
|
partialDevmode->dmDriverExtra = 0;
|
|
|
|
/*
|
|
* If the above should fail (it seemed to under low memory conditions),
|
|
* then we'd better have a valid size or the following memory copies
|
|
* will be wrong.
|
|
*/
|
|
partialDevmode->dmSize = sizeof(DEVMODEW);
|
|
|
|
// if (G_TERM(pDispInfo)->hdcScreen)
|
|
// {
|
|
// //
|
|
// // Use the caps as a guess for this.
|
|
// //
|
|
//
|
|
// WARNING("Drv_Trace: CaptMatchDevmode: Could not get current devmode\n");
|
|
//
|
|
// partialDevmode->dmBitsPerPel =
|
|
// GreGetDeviceCaps(G_TERM(pDispInfo)->hdcScreen, BITSPIXEL) *
|
|
// GreGetDeviceCaps(G_TERM(pDispInfo)->hdcScreen, PLANES);
|
|
// partialDevmode->dmPelsWidth =
|
|
// GreGetDeviceCaps(G_TERM(pDispInfo)->hdcScreen, HORZRES);
|
|
// partialDevmode->dmPelsHeight =
|
|
// GreGetDeviceCaps(G_TERM(pDispInfo)->hdcScreen, VERTRES);
|
|
// partialDevmode->dmDisplayFrequency =
|
|
// GreGetDeviceCaps(G_TERM(pDispInfo)->hdcScreen, VREFRESH);
|
|
// }
|
|
}
|
|
|
|
if ((SourceDevmode->dmFields & DM_BITSPERPEL) &&
|
|
(SourceDevmode->dmBitsPerPel != 0))
|
|
{
|
|
partialDevmode->dmBitsPerPel = SourceDevmode->dmBitsPerPel;
|
|
}
|
|
|
|
if ((SourceDevmode->dmFields & DM_PELSWIDTH) &&
|
|
(SourceDevmode->dmPelsWidth != 0))
|
|
{
|
|
partialDevmode->dmPelsWidth = SourceDevmode->dmPelsWidth;
|
|
}
|
|
|
|
if ((SourceDevmode->dmFields & DM_PELSHEIGHT) &&
|
|
(SourceDevmode->dmPelsHeight != 0))
|
|
{
|
|
partialDevmode->dmPelsHeight = SourceDevmode->dmPelsHeight;
|
|
}
|
|
|
|
if ((SourceDevmode->dmFields & DM_DISPLAYFREQUENCY) &&
|
|
(SourceDevmode->dmDisplayFrequency != 0))
|
|
{
|
|
partialDevmode->dmDisplayFrequency = SourceDevmode->dmDisplayFrequency;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Only use the registry refresh rate if we are going
|
|
// down in resolution. If we are going up in resolution,
|
|
// we will want to pick the lowest refresh rate that
|
|
// makes sense.
|
|
//
|
|
// The exception to this is if we have resetting the mode
|
|
// to the regsitry mode (passing in all 0's), in which case
|
|
// we want exactly what is in the registry.
|
|
//
|
|
|
|
if ( ((SourceDevmode->dmPelsWidth != 0) ||
|
|
(SourceDevmode->dmPelsHeight != 0)) )
|
|
{
|
|
partialDevmode->dmDisplayFrequency = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
btmpError = FALSE;
|
|
|
|
//
|
|
// These fields are somewhat optional.
|
|
// We capture them if they are valid. Otherwise, they will
|
|
// be initialized back to zero.
|
|
//
|
|
|
|
//
|
|
// Pick whichever set of flags we can. Source is first choice,
|
|
// registry is second.
|
|
//
|
|
|
|
if (SourceDevmode->dmFields & DM_DISPLAYFLAGS)
|
|
{
|
|
if (SourceDevmode->dmDisplayFlags & (~DMDISPLAYFLAGS_VALID))
|
|
{
|
|
btmpError = TRUE;
|
|
}
|
|
tmpDisplayFlags = SourceDevmode->dmDisplayFlags;
|
|
}
|
|
else if ((partialDevmode->dmFields & DM_DISPLAYFLAGS) &&
|
|
(partialDevmode->dmDisplayFlags &
|
|
(~DMDISPLAYFLAGS_VALID)))
|
|
{
|
|
tmpDisplayFlags = partialDevmode->dmDisplayFlags;
|
|
}
|
|
|
|
//
|
|
// If the caller specified panning keep the value, unless it was
|
|
// bigger than the resolution, which is an error.
|
|
//
|
|
// Otherwise, use the value from the registry if it makes sense
|
|
// (i.e. panning is still smaller than the resolution).
|
|
//
|
|
|
|
if ((SourceDevmode->dmFields & DM_PANNINGWIDTH) &&
|
|
(SourceDevmode->dmFields & DM_PANNINGHEIGHT))
|
|
{
|
|
if ((SourceDevmode->dmPanningWidth > partialDevmode->dmPelsWidth) ||
|
|
(SourceDevmode->dmPanningHeight > partialDevmode->dmPelsHeight))
|
|
{
|
|
btmpError = TRUE;
|
|
}
|
|
tmpPanningWidth = SourceDevmode->dmPanningWidth;
|
|
tmpPanningHeight = SourceDevmode->dmPanningHeight;
|
|
}
|
|
else if ((partialDevmode->dmFields & DM_PANNINGWIDTH) &&
|
|
(partialDevmode->dmFields & DM_PANNINGHEIGHT) &&
|
|
(partialDevmode->dmPanningHeight < partialDevmode->dmPelsHeight) &&
|
|
(partialDevmode->dmPanningWidth < partialDevmode->dmPelsWidth))
|
|
{
|
|
tmpPanningWidth = partialDevmode->dmPanningWidth;
|
|
tmpPanningHeight = partialDevmode->dmPanningHeight;
|
|
}
|
|
|
|
//
|
|
// Check the orientation.
|
|
//
|
|
|
|
if (SourceDevmode->dmFields & DM_DISPLAYORIENTATION)
|
|
{
|
|
bOrientationSpecified = TRUE;
|
|
partialDevmode->dmDisplayOrientation = SourceDevmode->dmDisplayOrientation;
|
|
|
|
if (SourceDevmode->dmDisplayOrientation > DMDO_LAST)
|
|
{
|
|
btmpError = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check fixed output mode.
|
|
//
|
|
|
|
if (SourceDevmode->dmFields & DM_DISPLAYFIXEDOUTPUT)
|
|
{
|
|
partialDevmode->dmDisplayFixedOutput = SourceDevmode->dmDisplayFixedOutput;
|
|
|
|
// DMDFO_DEFAULT means the first driver reported devmode
|
|
// matching the other parameters is to be picked; so, in
|
|
// that case bFixedOutputSpecified remains FALSE.
|
|
if (SourceDevmode->dmDisplayFixedOutput != DMDFO_DEFAULT)
|
|
{
|
|
bFixedOutputSpecified = TRUE;
|
|
if (SourceDevmode->dmDisplayFixedOutput > DMDFO_LAST)
|
|
{
|
|
btmpError = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Capture the position.
|
|
//
|
|
// If the size of the rectangle is NULL, that means the device
|
|
// needs to be detached from the desktop.
|
|
//
|
|
|
|
if (SourceDevmode->dmFields & DM_POSITION)
|
|
{
|
|
tmpPosition = TRUE;
|
|
tmpPositionX = SourceDevmode->dmPosition.x;
|
|
tmpPositionY = SourceDevmode->dmPosition.y;
|
|
}
|
|
else
|
|
{
|
|
tmpPosition = partialDevmode->dmFields & DM_POSITION;
|
|
tmpPositionX = partialDevmode->dmPosition.x;
|
|
tmpPositionY = partialDevmode->dmPosition.y;
|
|
}
|
|
|
|
|
|
//
|
|
// If the PhysDisp attaches to a device which removable, never put
|
|
// the primary desktop on it. And never put origin (0,0) on it
|
|
//
|
|
if (PhysDisp->stateFlags & DISPLAY_DEVICE_REMOVABLE)
|
|
{
|
|
if (tmpPosition &&
|
|
tmpPositionX == 0 &&
|
|
tmpPositionY == 0 )
|
|
{
|
|
TRACE_INIT(("Drv_Trace: CaptMatchDevmode: User tried to put the primary desktop on a removable device.\n"));
|
|
|
|
VFREEMEM(partialDevmode);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
if (btmpError == TRUE)
|
|
{
|
|
//
|
|
// The panning values or the flags are invalid
|
|
//
|
|
|
|
RIP("Drv_Trace: CaptMatchDevmode: Invalid Optional DEVMODE fields\n");
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Allocate enough memory so we can store the whole devmode.
|
|
//
|
|
|
|
sizeExtra = sourceSizeExtra;
|
|
if (sizeExtra == 0)
|
|
{
|
|
sizeExtra = partialDevmode->dmDriverExtra;
|
|
}
|
|
|
|
matchedDevmode = (PDEVMODEW) PALLOCMEM(sizeof(DEVMODEW) + sizeExtra,
|
|
GDITAG_DEVMODE);
|
|
|
|
if (matchedDevmode)
|
|
{
|
|
//
|
|
// Let's copy any DriverExtra information that the
|
|
// application may have passed down while we are still in
|
|
// the try\except. If we fail the call later, the memory
|
|
// will get deallocated anyways.
|
|
//
|
|
// If the application did not specify any such data, then
|
|
// copy it from the registry.
|
|
//
|
|
|
|
if (sourceSizeExtra)
|
|
{
|
|
RtlCopyMemory(matchedDevmode + 1,
|
|
(PUCHAR)SourceDevmode + sourceSize,
|
|
sizeExtra);
|
|
}
|
|
else if (partialDevmode->dmDriverExtra)
|
|
{
|
|
RtlCopyMemory(matchedDevmode + 1,
|
|
(PUCHAR)partialDevmode + partialDevmode->dmSize,
|
|
sizeExtra);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TRACE_INIT(("Drv_Trace: CaptMatchDevmode: Capture Complete\n"));
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
WARNINGX(101);
|
|
|
|
//
|
|
// If we hit an exception, free the buffer we have allocated.
|
|
//
|
|
|
|
if (matchedDevmode)
|
|
{
|
|
VFREEMEM(matchedDevmode);
|
|
}
|
|
|
|
matchedDevmode = NULL;
|
|
}
|
|
|
|
//
|
|
// This is our matching algorithm, based on requirements from Win95.
|
|
//
|
|
// As a rule, a value in the DEVMODE is only valid when BOTH the value is
|
|
// non-zero, and the dmFields flag is set. Otherwise, the value from the
|
|
// registry must be used
|
|
//
|
|
// For X, Y and color depth, we will follow this rule.
|
|
//
|
|
// For the refresh rate, we are just trying to find something that works
|
|
// for the screen. We are far from guaranteed that the refresh rate in
|
|
// the registry will be found for the X and Y we have since refresh rates
|
|
// vary a lot from mode to mode.
|
|
//
|
|
// So if the value is not specifically set and we do not find the exact
|
|
// value from the reigstry in the new resolution, Then we will try 60 Hz.
|
|
// We just want to get something that works MOST of the time so that the
|
|
// user does not get a mode that does not work.
|
|
//
|
|
// For the other fields (dmDisplayFlags, and panning), we just pass on what
|
|
// the application specified, and it's up to the driver to parse those,
|
|
// fields appropriatly.
|
|
//
|
|
|
|
//
|
|
// Now lets enumerate all the DEVMODEs and see if we have one
|
|
// that matches what we need.
|
|
//
|
|
|
|
if (matchedDevmode)
|
|
{
|
|
typedef enum {
|
|
NO_MATCH,
|
|
DEFAULT_MATCH,
|
|
EXACT_MATCH
|
|
} MatchLevel;
|
|
|
|
PDEVMODEW pdevmodeMatch = NULL;
|
|
MatchLevel eOrientationMatch = NO_MATCH;
|
|
MatchLevel eFixedOutputMatch = NO_MATCH;
|
|
MatchLevel eFrequencyMatch = NO_MATCH;
|
|
BOOL bExactMatch = FALSE;
|
|
PDEVMODEW pdevmodeInfo;
|
|
|
|
TRACE_INIT(("Drv_Trace: CaptMatchDevmode: Start matching\n"));
|
|
|
|
DrvBuildDevmodeList(PhysDisp, FALSE);
|
|
|
|
pdevmodeInfo = PhysDisp->devmodeInfo;
|
|
|
|
//
|
|
// If we can not find a mode because the caller was asking for the
|
|
// default mode, then just pick any 640x480 mode.
|
|
// We are not worried about picking a nice default because the applet
|
|
// generally takes care of that during configuration.
|
|
//
|
|
// In the case of hydra, the first mode in the driver is picked - why ?
|
|
//
|
|
// For mirroring, the mode for the primary device should be used.
|
|
// A special error code will be used for that
|
|
//
|
|
|
|
if ((partialDevmode->dmBitsPerPel == 0) &&
|
|
(partialDevmode->dmPelsWidth == 0) &&
|
|
(partialDevmode->dmPelsHeight == 0) &&
|
|
(partialDevmode->dmDisplayOrientation == DMDO_DEFAULT))
|
|
{
|
|
WARNING("Drv_Trace: CaptMatchDevmode: DEFAULT DEVMODE picked\n");
|
|
|
|
if (PhysDisp->stateFlags & (DISPLAY_DEVICE_DISCONNECT | DISPLAY_DEVICE_REMOTE))
|
|
{
|
|
//
|
|
// Hydra only supports one mode.
|
|
//
|
|
|
|
if (PhysDisp->devmodeInfo)
|
|
{
|
|
partialDevmode->dmBitsPerPel = PhysDisp->devmodeInfo->dmBitsPerPel;
|
|
partialDevmode->dmPelsWidth = PhysDisp->devmodeInfo->dmPelsWidth;
|
|
partialDevmode->dmPelsHeight = PhysDisp->devmodeInfo->dmPelsHeight;
|
|
partialDevmode->dmDisplayFrequency = PhysDisp->devmodeInfo->dmDisplayFrequency;
|
|
partialDevmode->dmDisplayOrientation = PhysDisp->devmodeInfo->dmDisplayOrientation;
|
|
partialDevmode->dmDisplayFixedOutput = PhysDisp->devmodeInfo->dmDisplayFixedOutput;
|
|
}
|
|
}
|
|
else if (PhysDisp->stateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER)
|
|
{
|
|
//
|
|
// For the mirror driver:
|
|
// If we have no modes in the driver, fail.
|
|
// Otherwise, return the default mode the driver provides.
|
|
//
|
|
|
|
if (PhysDisp->cbdevmodeInfo == 0)
|
|
{
|
|
ntRet = STATUS_INVALID_PARAMETER_MIX;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
partialDevmode->dmBitsPerPel = 0;
|
|
partialDevmode->dmPelsWidth = 640;
|
|
partialDevmode->dmPelsHeight = 480;
|
|
if (bClosest)
|
|
{
|
|
LPDEVMODEW pdm = GetClosestMode(PhysDisp, partialDevmode, bPrune);
|
|
if (pdm != NULL)
|
|
{
|
|
partialDevmode->dmBitsPerPel = pdm->dmBitsPerPel;
|
|
partialDevmode->dmPelsWidth = pdm->dmPelsWidth;
|
|
partialDevmode->dmPelsHeight = pdm->dmPelsHeight;
|
|
partialDevmode->dmDisplayFrequency = pdm->dmDisplayFrequency;
|
|
partialDevmode->dmDisplayOrientation = pdm->dmDisplayOrientation;
|
|
partialDevmode->dmDisplayFixedOutput = pdm->dmDisplayFixedOutput;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// For mirror drivers:
|
|
// If the driver return no modes but something was specified in the
|
|
// registry, return that to the driver.
|
|
//
|
|
|
|
else if ((PhysDisp->stateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) &&
|
|
(PhysDisp->cbdevmodeInfo == 0))
|
|
{
|
|
pdevmodeMatch = partialDevmode;
|
|
|
|
ASSERTGDI(PhysDisp->cbdevmodeInfo == 0, "MIRROR driver must have no modes\n");
|
|
}
|
|
else if (bClosest)
|
|
{
|
|
LPDEVMODEW pdm = GetClosestMode(PhysDisp, partialDevmode, bPrune);
|
|
if (pdm != NULL)
|
|
{
|
|
partialDevmode->dmBitsPerPel = pdm->dmBitsPerPel;
|
|
partialDevmode->dmPelsWidth = pdm->dmPelsWidth;
|
|
partialDevmode->dmPelsHeight = pdm->dmPelsHeight;
|
|
partialDevmode->dmDisplayFrequency = pdm->dmDisplayFrequency;
|
|
partialDevmode->dmDisplayOrientation = pdm->dmDisplayOrientation;
|
|
partialDevmode->dmDisplayFixedOutput = pdm->dmDisplayFixedOutput;
|
|
}
|
|
}
|
|
|
|
|
|
for (ULONG i = 0; i < PhysDisp->numRawModes; i++)
|
|
{
|
|
if (bPrune && PhysDisp->devmodeMarks[i].bPruned)
|
|
continue;
|
|
pdevmodeInfo = PhysDisp->devmodeMarks[i].pDevMode;
|
|
if (((partialDevmode->dmBitsPerPel == 0) ||
|
|
(partialDevmode->dmBitsPerPel == pdevmodeInfo->dmBitsPerPel)) &&
|
|
(partialDevmode->dmPelsWidth == pdevmodeInfo->dmPelsWidth) &&
|
|
(partialDevmode->dmPelsHeight == pdevmodeInfo->dmPelsHeight) &&
|
|
((partialDevmode->dmDisplayOrientation == pdevmodeInfo->dmDisplayOrientation) ||
|
|
(
|
|
(eOrientationMatch != EXACT_MATCH) &&
|
|
!bOrientationSpecified &&
|
|
(
|
|
(eOrientationMatch != DEFAULT_MATCH) ||
|
|
(pdevmodeInfo->dmDisplayOrientation == DMDO_DEFAULT))
|
|
)) &&
|
|
(!bFixedOutputSpecified ||
|
|
(partialDevmode->dmDisplayFixedOutput == pdevmodeInfo->dmDisplayFixedOutput))
|
|
)
|
|
{
|
|
//
|
|
// Pick at least the first mode that matches the resolution
|
|
// so that we at least have a chance at working.
|
|
//
|
|
// Then pick 60 Hz if we find it.
|
|
//
|
|
// Even better, pick the refresh that matches the current
|
|
// refresh (we assume that what's in the registry has the
|
|
// best chance of working.
|
|
//
|
|
|
|
if (pdevmodeMatch == NULL)
|
|
{
|
|
pdevmodeMatch = pdevmodeInfo;
|
|
}
|
|
|
|
if ((eOrientationMatch == NO_MATCH) &&
|
|
(pdevmodeInfo->dmDisplayOrientation == DMDO_DEFAULT))
|
|
{
|
|
pdevmodeMatch = pdevmodeInfo;
|
|
|
|
eOrientationMatch = DEFAULT_MATCH;
|
|
eFixedOutputMatch = NO_MATCH;
|
|
eFrequencyMatch = NO_MATCH;
|
|
}
|
|
|
|
if ((eOrientationMatch != EXACT_MATCH) &&
|
|
(partialDevmode->dmDisplayOrientation ==
|
|
pdevmodeInfo->dmDisplayOrientation))
|
|
{
|
|
pdevmodeMatch = pdevmodeInfo;
|
|
|
|
eOrientationMatch = EXACT_MATCH;
|
|
eFixedOutputMatch = NO_MATCH;
|
|
eFrequencyMatch = NO_MATCH;
|
|
}
|
|
|
|
// Fixed Output setting only matches exactly when specified,
|
|
// but matches first when not specified (when field flag isn't
|
|
// set or dmDisplayFixedOutput == DMDFO_DEFAULT.)
|
|
if ((eFixedOutputMatch != EXACT_MATCH) &&
|
|
(!bFixedOutputSpecified ||
|
|
(partialDevmode->dmDisplayFixedOutput ==
|
|
pdevmodeInfo->dmDisplayFixedOutput)
|
|
))
|
|
{
|
|
pdevmodeMatch = pdevmodeInfo;
|
|
|
|
eFixedOutputMatch = EXACT_MATCH;
|
|
eFrequencyMatch = NO_MATCH;
|
|
}
|
|
|
|
if ((eFrequencyMatch == NO_MATCH) &&
|
|
(pdevmodeInfo->dmDisplayFrequency == 60))
|
|
{
|
|
pdevmodeMatch = pdevmodeInfo;
|
|
|
|
eFrequencyMatch = DEFAULT_MATCH;
|
|
}
|
|
|
|
if ((eFrequencyMatch != EXACT_MATCH) &&
|
|
(partialDevmode->dmDisplayFrequency ==
|
|
pdevmodeInfo->dmDisplayFrequency))
|
|
{
|
|
//
|
|
// We found even better than 60 - an exact frequency match !
|
|
//
|
|
|
|
eFrequencyMatch = EXACT_MATCH;
|
|
|
|
pdevmodeMatch = pdevmodeInfo;
|
|
|
|
if (eOrientationMatch == EXACT_MATCH &&
|
|
eFixedOutputMatch == EXACT_MATCH)
|
|
{
|
|
bExactMatch = TRUE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// For now, we ignore these other fields since they
|
|
// considered optional.
|
|
//
|
|
|
|
// pdevmodeInfo->dmDisplayFlags;
|
|
// pdevmodeInfo->dmPanningWidth;
|
|
// pdevmodeInfo->dmPanningHeight;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Always set these flags since we initialize the values.
|
|
// We need consistent flags all the time to avoid extra modesets
|
|
//
|
|
// Also, force font size to be static for now.
|
|
//
|
|
|
|
if (pdevmodeMatch != NULL)
|
|
{
|
|
RtlCopyMemory(matchedDevmode,
|
|
pdevmodeMatch,
|
|
pdevmodeMatch->dmSize);
|
|
|
|
matchedDevmode->dmDriverExtra = (WORD) sizeExtra;
|
|
matchedDevmode->dmLogPixels = (partialDevmode->dmLogPixels == 0) ?
|
|
96 :
|
|
partialDevmode->dmLogPixels;
|
|
|
|
matchedDevmode->dmFields |= (DM_PANNINGHEIGHT |
|
|
DM_PANNINGWIDTH |
|
|
DM_DISPLAYFLAGS |
|
|
DM_LOGPIXELS);
|
|
|
|
//
|
|
// Check that the display driver specified all the other
|
|
// flags (res, color, frequency) properly.
|
|
//
|
|
|
|
if ((matchedDevmode->dmFields & DM_INTERNAL_VALID_FLAGS) !=
|
|
DM_INTERNAL_VALID_FLAGS)
|
|
{
|
|
RIP("Drv_Trace: CaptMatchDevmode: BAD DM FLAGS\n");
|
|
}
|
|
|
|
//
|
|
// Extra flag that determines if we should set the attach or
|
|
// detach flag
|
|
//
|
|
|
|
matchedDevmode->dmFields |= ((tmpPosition) ? DM_POSITION : 0);
|
|
|
|
//
|
|
// In the case of a good match, also use these extra values.
|
|
//
|
|
|
|
matchedDevmode->dmPosition.x = tmpPositionX;
|
|
matchedDevmode->dmPosition.y = tmpPositionY;
|
|
matchedDevmode->dmDisplayFlags = tmpDisplayFlags;
|
|
matchedDevmode->dmPanningWidth = tmpPanningWidth;
|
|
matchedDevmode->dmPanningHeight = tmpPanningHeight;
|
|
}
|
|
|
|
//
|
|
// MAJOR optimization : Do not free the list at this point.
|
|
// Many apps call EnumDisplaySettings, and for each mode call
|
|
// ChangeDisplaySettings with it to see if it can be changed
|
|
// dynamically. When we free the list here, it causes us to recreate
|
|
// the list for each mode we have in the list, which can take on
|
|
// the order of 30 seconds if there are multiple display drivers
|
|
// involved.
|
|
// Even if we keep the list here, it should properly get freed
|
|
// at the end of EnumDisplaySettings.
|
|
//
|
|
|
|
//
|
|
// Exit path
|
|
//
|
|
|
|
if (pdevmodeMatch != NULL)
|
|
{
|
|
TRACE_INIT(("Drv_Trace: CaptMatchDevmode: Matched DEVMODE\n"));
|
|
dbgDumpDevmode(matchedDevmode);
|
|
|
|
*DestinationDevmode = matchedDevmode;
|
|
|
|
ntRet = (bExactMatch) ? STATUS_SUCCESS :
|
|
(((eFrequencyMatch != EXACT_MATCH) &&
|
|
partialDevmode->dmDisplayFrequency) ?
|
|
STATUS_INVALID_PARAMETER :
|
|
((eFrequencyMatch == EXACT_MATCH) ?
|
|
STATUS_SUCCESS :
|
|
STATUS_RECEIVE_PARTIAL));
|
|
}
|
|
else
|
|
{
|
|
VFREEMEM(matchedDevmode);
|
|
}
|
|
}
|
|
|
|
VFREEMEM(partialDevmode);
|
|
|
|
if (NT_SUCCESS(ntRet))
|
|
{
|
|
if (ntRet == STATUS_RECEIVE_PARTIAL)
|
|
{
|
|
TRACE_INIT(("Drv_Trace: CaptMatchDevmode: Exit partial success\n\n"));
|
|
}
|
|
else
|
|
{
|
|
TRACE_INIT(("Drv_Trace: CaptMatchDevmode: Exit exact success\n\n"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRACE_INIT(("Drv_Trace: CaptMatchDevmode: Exit error\n\n"));
|
|
}
|
|
|
|
return (ntRet);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrvGetVideoPowerState
|
|
*
|
|
* History:
|
|
* 02-Dec-1996 AndreVa Created.
|
|
\***************************************************************************/
|
|
|
|
NTSTATUS
|
|
DrvGetMonitorPowerState(
|
|
PMDEV pmdev,
|
|
DEVICE_POWER_STATE PowerState)
|
|
{
|
|
NTSTATUS Status = STATUS_UNSUCCESSFUL;
|
|
VIDEO_POWER_STATE VideoPowerState = (VIDEO_POWER_STATE) PowerState;
|
|
ULONG BytesReturned;
|
|
ULONG i;
|
|
|
|
#ifdef _HYDRA_
|
|
if (gProtocolType != PROTOCOL_CONSOLE ) {
|
|
//
|
|
// Don't Call this for remote video drivers
|
|
//
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
#endif
|
|
|
|
for (i = 0; i < pmdev->chdev; i++)
|
|
{
|
|
PDEVOBJ pdo(pmdev->Dev[i].hdev);
|
|
|
|
if ((pdo.ppdev->pGraphicsDevice->stateFlags & DISPLAY_DEVICE_DISCONNECT) != 0 ) {
|
|
//
|
|
// Don't Call this for disconnect drivers
|
|
//
|
|
ASSERT(pdo.ppdev->pGraphicsDevice->pDeviceHandle == NULL );
|
|
continue;
|
|
}
|
|
|
|
Status = GreDeviceIoControl(pdo.ppdev->pGraphicsDevice->pDeviceHandle,
|
|
IOCTL_VIDEO_GET_OUTPUT_DEVICE_POWER_STATE,
|
|
&VideoPowerState,
|
|
sizeof(VideoPowerState),
|
|
NULL,
|
|
0,
|
|
&BytesReturned);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrvSetVideoPowerState
|
|
*
|
|
* History:
|
|
* 02-Dec-1996 AndreVa Created.
|
|
\***************************************************************************/
|
|
|
|
NTSTATUS
|
|
DrvSetMonitorPowerState(
|
|
PMDEV pmdev,
|
|
DEVICE_POWER_STATE PowerState)
|
|
{
|
|
NTSTATUS Status = STATUS_UNSUCCESSFUL;
|
|
VIDEO_POWER_STATE VideoPowerState = (VIDEO_POWER_STATE) PowerState;
|
|
ULONG BytesReturned;
|
|
ULONG i;
|
|
|
|
#ifdef _HYDRA_
|
|
if (gProtocolType != PROTOCOL_CONSOLE ) {
|
|
//
|
|
// Don't Call this for remote video drivers
|
|
//
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
#endif
|
|
|
|
for (i = 0; i < pmdev->chdev; i++)
|
|
{
|
|
PDEVOBJ pdo(pmdev->Dev[i].hdev);
|
|
|
|
#ifdef _HYDRA_
|
|
|
|
if (pdo.ppdev->pGraphicsDevice->stateFlags & DISPLAY_DEVICE_DISCONNECT) {
|
|
//
|
|
// Don't Call this for disconnect drivers
|
|
//
|
|
if (pdo.ppdev->pGraphicsDevice->pDeviceHandle) {
|
|
TRACE_INIT(("DrvSetMonitorPowerState: Disconnect DD has a Device Handle = %p!\n",
|
|
pdo.ppdev->pGraphicsDevice->pDeviceHandle));
|
|
#if DBG
|
|
DbgBreakPoint();
|
|
#endif
|
|
}
|
|
continue;
|
|
}
|
|
#endif
|
|
Status = GreDeviceIoControl(pdo.ppdev->pGraphicsDevice->pDeviceHandle,
|
|
IOCTL_VIDEO_SET_OUTPUT_DEVICE_POWER_STATE,
|
|
&VideoPowerState,
|
|
sizeof(VideoPowerState),
|
|
NULL,
|
|
0,
|
|
&BytesReturned);
|
|
|
|
// !!! partial state problem ...
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrvQueryMDEVPowerState(
|
|
*
|
|
* History:
|
|
* 26-Jul-1998 AndreVa Created.
|
|
\***************************************************************************/
|
|
|
|
BOOL
|
|
DrvQueryMDEVPowerState(
|
|
PMDEV pmdev
|
|
)
|
|
{
|
|
ULONG i;
|
|
HDEV hdev;
|
|
|
|
for (i=0; i < pmdev->chdev; i++)
|
|
{
|
|
PDEVOBJ pdo(pmdev->Dev[i].hdev);
|
|
|
|
if (pdo.ppdev->pGraphicsDevice->stateFlags & DISPLAY_DEVICE_POWERED_OFF)
|
|
{
|
|
TRACE_INIT(("Drv_Trace: DrvQueryMDEVPowerState is OFF\n"));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvQueryMDEVPowerState is ON\n"));
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
DrvSetMDEVPowerState(
|
|
PMDEV pmdev,
|
|
BOOL On
|
|
)
|
|
{
|
|
TRACE_INIT(("Drv_Trace: DrvSetMDEVPowerState MDEV %p %s\n",
|
|
pmdev, On ? "ON" : "OFF"));
|
|
|
|
ULONG i;
|
|
HDEV hdev;
|
|
|
|
for (i=0; i < pmdev->chdev; i++)
|
|
{
|
|
PDEVOBJ pdo(pmdev->Dev[i].hdev);
|
|
|
|
if (On) {
|
|
pdo.ppdev->pGraphicsDevice->stateFlags &= ~DISPLAY_DEVICE_POWERED_OFF;
|
|
} else {
|
|
pdo.ppdev->pGraphicsDevice->stateFlags |= DISPLAY_DEVICE_POWERED_OFF;
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrvDisplaySwitchHandler()
|
|
*
|
|
* Return Value:
|
|
* TRUE: System needs to update to a new mode
|
|
* FALSE: The current mode works fine
|
|
*
|
|
* History:
|
|
* Dennyd Created
|
|
\***************************************************************************/
|
|
#define PRUNEMODE_REGKEY L"PruningMode" // PruneMode Flag
|
|
#define PRUNEMODE_BUFSIZE (sizeof(KEY_VALUE_FULL_INFORMATION) + sizeof(PRUNEMODE_REGKEY) + sizeof(DWORD))
|
|
|
|
BOOL
|
|
DrvGetPruneFlag(PGRAPHICS_DEVICE PhysDisp)
|
|
{
|
|
HANDLE hkRegistry;
|
|
DWORD PrunningMode = 1;
|
|
BYTE buffer[PRUNEMODE_BUFSIZE];
|
|
PKEY_VALUE_FULL_INFORMATION Information = (PKEY_VALUE_FULL_INFORMATION)buffer;
|
|
ULONG Length = PRUNEMODE_BUFSIZE;
|
|
|
|
hkRegistry = DrvGetRegistryHandleFromDeviceMap(
|
|
PhysDisp,
|
|
DispDriverRegGlobal,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
gProtocolType);
|
|
|
|
if (hkRegistry)
|
|
{
|
|
UNICODE_STRING UnicodeString;
|
|
RtlInitUnicodeString(&UnicodeString, PRUNEMODE_REGKEY);
|
|
|
|
if (NT_SUCCESS(ZwQueryValueKey(hkRegistry,
|
|
&UnicodeString,
|
|
KeyValueFullInformation,
|
|
Information,
|
|
Length,
|
|
&Length)))
|
|
{
|
|
PrunningMode = *(LPDWORD) ((((PUCHAR)Information) +
|
|
Information->DataOffset));
|
|
}
|
|
ZwCloseKey(hkRegistry);
|
|
}
|
|
return (PrunningMode != 0);
|
|
}
|
|
|
|
BOOL
|
|
DrvDisplaySwitchHandler(
|
|
PVOID physDisp, // IN
|
|
PUNICODE_STRING pstrDeviceName, // OUT
|
|
LPDEVMODEW pNewMode, // OUT
|
|
PULONG pPrune // OUT
|
|
)
|
|
{
|
|
DEVMODEW srcMode;
|
|
LPDEVMODEW lpModeWanted;
|
|
PGRAPHICS_DEVICE PhysDisp = (PGRAPHICS_DEVICE)physDisp;
|
|
ULONG i;
|
|
BOOL uu, bPrune;
|
|
|
|
TRACE_INIT(("DrvDisplaySwitchHandler: Enter\n"));
|
|
|
|
gbUpdateMonitor = TRUE;
|
|
UpdateMonitorDevices();
|
|
|
|
//
|
|
// On display switching, update mode list according to new active displays
|
|
//
|
|
DrvBuildDevmodeList(PhysDisp, TRUE);
|
|
|
|
bPrune = DrvGetPruneFlag(PhysDisp);
|
|
*pPrune = bPrune;
|
|
|
|
//
|
|
// Check if the mode in registry complies with the new mode list
|
|
// This is neccessary because when the system was first setup, there is no reg value
|
|
// under per monitor registry.
|
|
//
|
|
RtlZeroMemory(&srcMode, sizeof(DEVMODEW));
|
|
srcMode.dmSize = sizeof(DEVMODEW);
|
|
if (NT_SUCCESS (DrvProbeAndCaptureDevmode(PhysDisp,
|
|
&lpModeWanted,
|
|
&uu,
|
|
&srcMode,
|
|
FALSE,
|
|
KernelMode,
|
|
bPrune,
|
|
TRUE,
|
|
TRUE) )
|
|
)
|
|
{
|
|
TRACE_INIT(("DrvDisplaySwitchHandler: Pick reg mode B=%d W=%d H=%d F=%d R=%lu\n",
|
|
lpModeWanted->dmBitsPerPel, lpModeWanted->dmPelsWidth, lpModeWanted->dmPelsHeight, lpModeWanted->dmDisplayFrequency, lpModeWanted->dmDisplayOrientation*90));
|
|
|
|
RtlCopyMemory(pNewMode, lpModeWanted, sizeof(DEVMODEW));
|
|
RtlInitUnicodeString(pstrDeviceName, &(PhysDisp->szWinDeviceName[0]));
|
|
|
|
{
|
|
//
|
|
// If there is only one output device, update per monitor settings as well
|
|
//
|
|
ULONG activePdos = 0;
|
|
for (i = 0; i < PhysDisp->numMonitorDevice; i++)
|
|
{
|
|
if (IS_ATTACHED_ACTIVE(PhysDisp->MonitorDevices[i].flag))
|
|
{
|
|
activePdos++;
|
|
}
|
|
}
|
|
DrvUpdateDisplayDriverParameters(PhysDisp, lpModeWanted, FALSE, (activePdos == 1));
|
|
}
|
|
|
|
VFREEMEM(lpModeWanted);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
PVOID
|
|
DrvWakeupHandler(
|
|
HANDLE *ppdo // OUT
|
|
)
|
|
{
|
|
PGRAPHICS_DEVICE PhysDisp;
|
|
|
|
for (PhysDisp = gpGraphicsDeviceList;
|
|
PhysDisp != NULL;
|
|
PhysDisp = PhysDisp->pNextGraphicsDevice)
|
|
{
|
|
if (PhysDisp->stateFlags & DISPLAY_DEVICE_ACPI)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (PhysDisp)
|
|
{
|
|
*ppdo = PhysDisp->pPhysDeviceHandle;
|
|
}
|
|
|
|
return (PVOID)PhysDisp;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* DrvSendPnPIrp
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
|
|
NTSTATUS
|
|
DrvSendPnPIrp(
|
|
PDEVICE_OBJECT pDeviceObject,
|
|
DEVICE_RELATION_TYPE relationType,
|
|
PDEVICE_RELATIONS *pDeviceRelations)
|
|
{
|
|
NTSTATUS status;
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
KEVENT event;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
//
|
|
// Anything else is illegal.
|
|
//
|
|
|
|
ASSERT(relationType == TargetDeviceRelation);
|
|
|
|
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
|
|
|
|
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
|
|
pDeviceObject,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
&event,
|
|
&IoStatusBlock);
|
|
|
|
if (Irp == NULL) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Set the default error code.
|
|
//
|
|
|
|
Irp->IoStatus.Status = IoStatusBlock.Status = STATUS_NOT_SUPPORTED;
|
|
|
|
IrpSp = IoGetNextIrpStackLocation(Irp);
|
|
IrpSp->MajorFunction = IRP_MJ_PNP;
|
|
IrpSp->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
|
|
|
|
IrpSp->Parameters.QueryDeviceRelations.Type = relationType;
|
|
|
|
//
|
|
// Call the filter driver.
|
|
//
|
|
|
|
status = IoCallDriver(pDeviceObject, Irp);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
|
|
status = IoStatusBlock.Status;
|
|
|
|
}
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
*pDeviceRelations = (PDEVICE_RELATIONS) IoStatusBlock.Information;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* __EnumDisplayQueryRoutine
|
|
*
|
|
* Callback to get the display driver name.
|
|
*
|
|
* CRIT not needed
|
|
*
|
|
* 12-Jan-1994 andreva created
|
|
\**************************************************************************/
|
|
|
|
NTSTATUS
|
|
__DisplayDriverQueryRoutine(
|
|
IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext)
|
|
{
|
|
//
|
|
// If the context value is NULL and the entry type is correct, then store
|
|
// the length of the value. Otherwise, copy the value to the specified
|
|
// memory.
|
|
//
|
|
|
|
PGRAPHICS_DEVICE PhysDisp = (PGRAPHICS_DEVICE) Context;
|
|
|
|
//
|
|
// Include workaround for vendors that don't understand MULTI_SZ !
|
|
//
|
|
|
|
if (ValueType != REG_MULTI_SZ)
|
|
{
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
TRACE_INIT(("Drv_Trace: __DisplayDriverQueryRoutine MULTISZ %ws = %ws\n", ValueName, ValueData));
|
|
|
|
if (!(PhysDisp->DisplayDriverNames = (LPWSTR) PALLOCNOZ(ValueLength + 2, GDITAG_DRVSUP)))
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlCopyMemory(PhysDisp->DisplayDriverNames, ValueData, ValueLength);
|
|
*((LPWSTR) (((PUCHAR)PhysDisp->DisplayDriverNames) + ValueLength)) = UNICODE_NULL;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
UNREFERENCED_PARAMETER(ValueName);
|
|
UNREFERENCED_PARAMETER(EntryContext);
|
|
}
|
|
|
|
NTSTATUS
|
|
__EnumDisplayQueryRoutine(
|
|
IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext)
|
|
{
|
|
//
|
|
// If the context value is NULL and the entry type is correct, then store
|
|
// the length of the value. Otherwise, copy the value to the specified
|
|
// memory.
|
|
//
|
|
|
|
PGRAPHICS_DEVICE PhysDisp = (PGRAPHICS_DEVICE) Context;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// If the value is a string with only a NULL value, then keep going
|
|
// to the next possible string.
|
|
// So only try to save the string for > 2 characters.
|
|
//
|
|
|
|
if (ValueLength > 2)
|
|
{
|
|
if (ValueType == REG_SZ)
|
|
{
|
|
if (PhysDisp->DeviceDescription == NULL)
|
|
{
|
|
TRACE_INIT(("Drv_Trace: __EnumDisplayQueryRoutine SZ %ws = %ws\n", ValueName, ValueData));
|
|
|
|
if (PhysDisp->DeviceDescription = (LPWSTR) PALLOCNOZ(ValueLength, GDITAG_DRVSUP))
|
|
{
|
|
RtlCopyMemory(PhysDisp->DeviceDescription, ValueData, ValueLength);
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
}
|
|
else if (ValueType == REG_BINARY)
|
|
{
|
|
if (PhysDisp->DeviceDescription == NULL)
|
|
{
|
|
TRACE_INIT(("Drv_Trace: __EnumDisplayQueryRoutine BINARY %ws = %ws\n", ValueName, ValueData));
|
|
|
|
if (PhysDisp->DeviceDescription = (LPWSTR) PALLOCNOZ(ValueLength + sizeof(WCHAR), GDITAG_DRVSUP))
|
|
{
|
|
RtlCopyMemory(PhysDisp->DeviceDescription, ValueData, ValueLength);
|
|
*((LPWSTR)((LPBYTE)PhysDisp->DeviceDescription + ValueLength)) = UNICODE_NULL;
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERTGDI(FALSE, "Drv_Trace: __EnumDisplayQueryRoutine: Invalid callout type\n");
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
|
|
UNREFERENCED_PARAMETER(ValueName);
|
|
UNREFERENCED_PARAMETER(EntryContext);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* IsVgaDevice
|
|
*
|
|
* Determine if the given PhysDisp is for the VGA.
|
|
*
|
|
* 13-Oct-1999 ericks Created
|
|
\***************************************************************************/
|
|
|
|
BOOLEAN
|
|
IsVgaDevice(
|
|
PGRAPHICS_DEVICE PhysDisp
|
|
)
|
|
|
|
{
|
|
BOOLEAN Result = FALSE;
|
|
ULONG bytesReturned;
|
|
NTSTATUS status;
|
|
|
|
status = GreDeviceIoControl(PhysDisp->pDeviceHandle,
|
|
IOCTL_VIDEO_IS_VGA_DEVICE,
|
|
NULL,
|
|
0,
|
|
&Result,
|
|
sizeof(Result),
|
|
&bytesReturned);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
return Result;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* DrvGetDeviceConfigurationInformation
|
|
*
|
|
* Get the registry parameters for the driver
|
|
*
|
|
* 30-Apr-1998 andreva created
|
|
\**************************************************************************/
|
|
|
|
VOID
|
|
DrvGetDeviceConfigurationInformation(
|
|
PGRAPHICS_DEVICE PhysDisp,
|
|
HANDLE hkRegistry,
|
|
BOOL bIsPdo
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
ULONG defaultValue = 0;
|
|
ULONG multiDriver = 0;
|
|
ULONG mirroring = 0;
|
|
ULONG vgaCompat = 0;
|
|
|
|
RTL_QUERY_REGISTRY_TABLE multiQueryTable[] = {
|
|
{__EnumDisplayQueryRoutine, RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_NOEXPAND,
|
|
SoftwareSettings[0], NULL, REG_NONE, NULL, 0},
|
|
{NULL, RTL_QUERY_REGISTRY_SUBKEY,
|
|
L"Settings", NULL, REG_NONE, NULL, 0},
|
|
{__DisplayDriverQueryRoutine, RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_NOEXPAND,
|
|
SoftwareSettings[1], NULL, REG_NONE, NULL, 0},
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT,
|
|
SoftwareSettings[2], &multiDriver, REG_DWORD, &defaultValue, 4},
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT,
|
|
SoftwareSettings[3], &mirroring, REG_DWORD, &defaultValue, 4},
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT,
|
|
SoftwareSettings[4], &vgaCompat, REG_DWORD, &defaultValue, 4},
|
|
|
|
// Device Description is optional, for 4.0 legacy only
|
|
|
|
{__EnumDisplayQueryRoutine, RTL_QUERY_REGISTRY_NOEXPAND,
|
|
SoftwareSettings[5], NULL, REG_NONE, NULL, 0},
|
|
{__EnumDisplayQueryRoutine, RTL_QUERY_REGISTRY_NOEXPAND,
|
|
SoftwareSettings[6], NULL, REG_NONE, NULL, 0},
|
|
{__EnumDisplayQueryRoutine, RTL_QUERY_REGISTRY_NOEXPAND,
|
|
SoftwareSettings[7], NULL, REG_NONE, NULL, 0},
|
|
{NULL, 0, NULL}
|
|
};
|
|
|
|
status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
|
|
(PWSTR)hkRegistry,
|
|
bIsPdo ?
|
|
&multiQueryTable[0] :
|
|
&multiQueryTable[2],
|
|
PhysDisp,
|
|
NULL);
|
|
|
|
if (NT_SUCCESS(status))
|
|
|
|
{
|
|
if (multiDriver)
|
|
PhysDisp->stateFlags |= DISPLAY_DEVICE_MULTI_DRIVER;
|
|
if (mirroring)
|
|
PhysDisp->stateFlags |= DISPLAY_DEVICE_MIRRORING_DRIVER;
|
|
|
|
//
|
|
// We must determine which graphics device actually has the VGA resources
|
|
//
|
|
if (vgaCompat && IsVgaDevice(PhysDisp))
|
|
PhysDisp->stateFlags |= DISPLAY_DEVICE_VGA_COMPATIBLE;
|
|
|
|
TRACE_INIT(("DrvGetDeviceConfigurationInformation: Display driver is %sa multi display driver\n",
|
|
multiDriver ? "" : "NOT "));
|
|
TRACE_INIT(("DrvGetDeviceConfigurationInformation: Display driver is %smirroring the desktop\n",
|
|
mirroring ? "" : "NOT "));
|
|
TRACE_INIT(("DrvGetDeviceConfigurationInformation: Display driver is %sVga Compatible\n",
|
|
vgaCompat ? "" : "NOT "));
|
|
}
|
|
else
|
|
{
|
|
//
|
|
//
|
|
// An error occured - this device is miss-configured.
|
|
//
|
|
|
|
TRACE_INIT(("DrvGetDeviceConfigurationInformation: Device is misconfigured\n"));
|
|
|
|
DrvLogDisplayDriverEvent(MsgInvalidConfiguration);
|
|
|
|
|
|
if (PhysDisp->DisplayDriverNames)
|
|
{
|
|
VFREEMEM(PhysDisp->DisplayDriverNames);
|
|
PhysDisp->DisplayDriverNames = NULL;
|
|
}
|
|
if (PhysDisp->DeviceDescription)
|
|
{
|
|
VFREEMEM(PhysDisp->DeviceDescription);
|
|
PhysDisp->DeviceDescription = NULL;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
* DrvSetSingleDisplay
|
|
*
|
|
* Remove all physical displays except for specified primary.
|
|
* If PhysDispPrimary == NULL, the list physical displays is
|
|
* searched for the marked display and secondly any one.
|
|
*
|
|
* 25-May-2000 jasonha created
|
|
\**************************************************************************/
|
|
|
|
BOOL bPrunedDisplayDevice = FALSE; // Was a secondary display devcice rejected?
|
|
|
|
PGRAPHICS_DEVICE
|
|
DrvSetSingleDisplay(
|
|
PGRAPHICS_DEVICE PhysDispPrimary
|
|
)
|
|
{
|
|
GDIFunctionID(DrvSetSingleDisplay);
|
|
|
|
PGRAPHICS_DEVICE PhysDispTemp;
|
|
PGRAPHICS_DEVICE PhysDispPrev = NULL;
|
|
PGRAPHICS_DEVICE PhysDispNext = NULL;
|
|
|
|
// Confirm the given primary is in the list
|
|
// or identify the marked primary.
|
|
for (PhysDispTemp = gpGraphicsDeviceList;
|
|
PhysDispTemp != NULL;
|
|
PhysDispTemp = PhysDispTemp->pNextGraphicsDevice)
|
|
{
|
|
if (! (PhysDispTemp->stateFlags & (DISPLAY_DEVICE_MIRRORING_DRIVER | DISPLAY_DEVICE_REMOTE | DISPLAY_DEVICE_DISCONNECT)))
|
|
{
|
|
if (PhysDispPrimary == NULL)
|
|
{
|
|
if (PhysDispTemp->stateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
|
|
{
|
|
PhysDispPrimary = PhysDispTemp;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (PhysDispTemp == PhysDispPrimary)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we haven't identified a primary,
|
|
// select any physical display.
|
|
if (PhysDispPrimary == NULL)
|
|
{
|
|
for (PhysDispTemp = gpGraphicsDeviceList;
|
|
PhysDispTemp != NULL;
|
|
PhysDispTemp = PhysDispTemp->pNextGraphicsDevice)
|
|
{
|
|
if (! (PhysDispTemp->stateFlags & (DISPLAY_DEVICE_MIRRORING_DRIVER | DISPLAY_DEVICE_REMOTE | DISPLAY_DEVICE_DISCONNECT)))
|
|
{
|
|
PhysDispPrimary = PhysDispTemp;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we didn't find the specified primary or a
|
|
// suitable one, then there is nothing to do.
|
|
if (PhysDispTemp == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Remove all physical secondary displays
|
|
PhysDispPrev = NULL;
|
|
|
|
for (PhysDispTemp = gpGraphicsDeviceList;
|
|
PhysDispTemp != NULL;
|
|
PhysDispTemp = PhysDispNext)
|
|
{
|
|
PhysDispNext = PhysDispTemp->pNextGraphicsDevice;
|
|
|
|
if (PhysDispTemp != PhysDispPrimary &&
|
|
! (PhysDispTemp->stateFlags & (DISPLAY_DEVICE_MIRRORING_DRIVER | DISPLAY_DEVICE_REMOTE | DISPLAY_DEVICE_DISCONNECT)))
|
|
{
|
|
ASSERTGDI(PhysDispTemp != gPhysDispVGA, "Removing VGA as a secondary.\n");
|
|
bPrunedDisplayDevice = TRUE;
|
|
|
|
// Remove from list
|
|
if (PhysDispPrev == NULL)
|
|
{
|
|
gpGraphicsDeviceList = PhysDispTemp->pNextGraphicsDevice;
|
|
}
|
|
else
|
|
{
|
|
PhysDispPrev->pNextGraphicsDevice = PhysDispTemp->pNextGraphicsDevice;
|
|
}
|
|
|
|
if (PhysDispTemp == gpGraphicsDeviceListLast)
|
|
{
|
|
gpGraphicsDeviceListLast = PhysDispPrev;
|
|
}
|
|
|
|
// Cleanup any allocations
|
|
if (PhysDispTemp->DisplayDriverNames)
|
|
VFREEMEM(PhysDispTemp->DisplayDriverNames);
|
|
|
|
if (PhysDispTemp->DeviceDescription)
|
|
VFREEMEM(PhysDispTemp->DeviceDescription);
|
|
|
|
if (PhysDispTemp->MonitorDevices)
|
|
VFREEMEM(PhysDispTemp->MonitorDevices);
|
|
|
|
if (PhysDispTemp->devmodeInfo)
|
|
VFREEMEM(PhysDispTemp->devmodeInfo);
|
|
|
|
if (PhysDispTemp->devmodeMarks)
|
|
VFREEMEM(PhysDispTemp->devmodeMarks);
|
|
|
|
VFREEMEM(PhysDispTemp);
|
|
|
|
// Reuse output numbers so next added display is in order.
|
|
gcNextGlobalPhysicalOutputNumber--;
|
|
}
|
|
else
|
|
{
|
|
PhysDispPrev = PhysDispTemp;
|
|
}
|
|
}
|
|
|
|
// Set Primary markings
|
|
if (PhysDispPrimary)
|
|
{
|
|
PhysDispPrimary->stateFlags |= DISPLAY_DEVICE_PRIMARY_DEVICE;
|
|
|
|
// Make sure this is display 1 for app compat.
|
|
wcscpy(&(PhysDispPrimary->szWinDeviceName[0]),
|
|
L"\\\\.\\DISPLAY1");
|
|
|
|
// If we loaded VGA and it is not the primary display, then
|
|
// rename it to display 2. It can't actually be referenced
|
|
// from APIs, but we can keep the numbers in order.
|
|
if (gPhysDispVGA && gPhysDispVGA != PhysDispPrimary)
|
|
{
|
|
// Rename VGA's WinDeviceName
|
|
wcscpy(&(gPhysDispVGA->szWinDeviceName[0]),
|
|
L"\\\\.\\DISPLAY2");
|
|
|
|
ASSERTGDI(gcNextGlobalPhysicalOutputNumber == 3,
|
|
"gcNextGlobalPhysicalOutputNumber != 3\n");
|
|
}
|
|
else
|
|
{
|
|
ASSERTGDI(gcNextGlobalPhysicalOutputNumber == 2,
|
|
"gcNextGlobalPhysicalOutputNumber != 2\n");
|
|
}
|
|
}
|
|
|
|
return PhysDispPrimary;
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************************\
|
|
* DrvUpdateVgaDevice
|
|
*
|
|
* Update the VGA graphics device in the machine.
|
|
*
|
|
* 01-Apr-1998 andreva created
|
|
\**************************************************************************/
|
|
|
|
PGRAPHICS_DEVICE
|
|
DrvUpdateVgaDevice()
|
|
{
|
|
PGRAPHICS_DEVICE PhysDispIsVga;
|
|
PGRAPHICS_DEVICE PhysDispMarkVga;
|
|
PGRAPHICS_DEVICE PhysDispTmp;
|
|
|
|
NTSTATUS Status;
|
|
VIDEO_NUM_MODES NumModes;
|
|
ULONG NumModesLength = sizeof(NumModes);
|
|
ULONG cbBuffer;
|
|
ULONG BytesReturned;
|
|
|
|
PVIDEO_MODE_INFORMATION lpModes;
|
|
PVIDEO_MODE_INFORMATION pVideoModeSave;
|
|
|
|
ULONG cTextModes = 0;
|
|
ULONG cGraphicsModes = 0;
|
|
|
|
LPDEVMODEW pUsDevmode;
|
|
LPDEVMODEW ptmpDevmode;
|
|
LPDEVMODEMARK pUsDevmodeMark;
|
|
|
|
BOOLEAN PrimaryExists = FALSE;
|
|
BOOL VGAInUse = FALSE;
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvUpdateVgaDevice: Finding VGA device\n"));
|
|
|
|
//
|
|
// Find the VGA compatible driver for the console.
|
|
//
|
|
// NOTE - These fullscreen modes are only supported on X86
|
|
//
|
|
|
|
//
|
|
// Clear out the VGA, and the VGA flag of the parent if necessary
|
|
//
|
|
|
|
for (PhysDispTmp = gpGraphicsDeviceList;
|
|
PhysDispTmp != NULL;
|
|
PhysDispTmp = PhysDispTmp->pNextGraphicsDevice)
|
|
{
|
|
PhysDispTmp->pVgaDevice = NULL;
|
|
|
|
if ((PhysDispTmp->stateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) &&
|
|
(PhysDispTmp != gPhysDispVGA)) {
|
|
|
|
//
|
|
// Determine if this device is currently a primary, and it
|
|
// is not the VGA. (The VGA may later be removed).
|
|
//
|
|
|
|
PrimaryExists = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If there is no primary device yet, lets choose the vga.
|
|
//
|
|
|
|
if (PrimaryExists == FALSE) {
|
|
|
|
for (PhysDispTmp = gpGraphicsDeviceList;
|
|
PhysDispTmp != NULL;
|
|
PhysDispTmp = PhysDispTmp->pNextGraphicsDevice) {
|
|
|
|
if (IsVgaDevice(PhysDispTmp)) {
|
|
|
|
PhysDispTmp->stateFlags |= DISPLAY_DEVICE_PRIMARY_DEVICE;
|
|
PrimaryExists = TRUE;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Find the VGA compatible device
|
|
//
|
|
|
|
for (PhysDispIsVga = gpGraphicsDeviceList;
|
|
PhysDispIsVga != NULL;
|
|
PhysDispIsVga = PhysDispIsVga->pNextGraphicsDevice)
|
|
{
|
|
//
|
|
// The vga driver is here for compatibility.
|
|
// Look for all other possibilities, and revert to VGA as a last
|
|
// possibility
|
|
//
|
|
|
|
if (PhysDispIsVga == gPhysDispVGA)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (PhysDispIsVga->stateFlags & DISPLAY_DEVICE_VGA_COMPATIBLE)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If no vga device was found, use the vga driver
|
|
//
|
|
|
|
if (PhysDispIsVga == NULL)
|
|
{
|
|
PhysDispIsVga = gPhysDispVGA;
|
|
}
|
|
|
|
//
|
|
// On some machines, with TGA for example, it's possible to have no
|
|
// VGA compatible device.
|
|
//
|
|
|
|
if (PhysDispIsVga == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Now determine if there is a duplicate device (like VGA)
|
|
//
|
|
// HACK - we only support the VGA as a duplicate device right now.
|
|
// We need more info for cirrus + #9
|
|
//
|
|
// VGA is a duplicate if there is ANOTHER driver in the machine, excluding
|
|
// for MIRROR DEVICES, Disconnected device and remote devices. Personal
|
|
// Windows also requires the driver to be the VGA device driver.
|
|
//
|
|
|
|
PhysDispMarkVga = PhysDispIsVga;
|
|
|
|
if (gPhysDispVGA)
|
|
{
|
|
PPDEV ppdev;
|
|
|
|
// Determine if VGA device is being used.
|
|
// We have to hold ghsemDriverMgmt to be sure its
|
|
// status doesn't change.
|
|
|
|
GreAcquireSemaphoreEx(ghsemDriverMgmt, SEMORDER_DRIVERMGMT, NULL);
|
|
|
|
for (ppdev = gppdevList; ppdev != NULL; ppdev = ppdev->ppdevNext)
|
|
{
|
|
if (ppdev->pGraphicsDevice == gPhysDispVGA)
|
|
{
|
|
VGAInUse = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If the VGA device's driver is non-VGA compatible
|
|
// (DISPLAY_DEVICE_VGA_COMPATIBLE was not marked on
|
|
// any device so
|
|
// PhysDispMarkVga = PhysDispIsVga = gPhysDispVGA),
|
|
// then search for it so it may be marked as the VGA
|
|
if (PhysDispMarkVga == gPhysDispVGA)
|
|
{
|
|
// Look for the real VGA device
|
|
for (PhysDispMarkVga = gpGraphicsDeviceList;
|
|
PhysDispMarkVga != NULL;
|
|
PhysDispMarkVga = PhysDispMarkVga->pNextGraphicsDevice
|
|
)
|
|
{
|
|
if ((PhysDispMarkVga != gPhysDispVGA) &&
|
|
((PhysDispMarkVga->stateFlags & (DISPLAY_DEVICE_MIRRORING_DRIVER |
|
|
DISPLAY_DEVICE_DISCONNECT |
|
|
DISPLAY_DEVICE_REMOTE) ) == 0) &&
|
|
IsVgaDevice(PhysDispMarkVga)
|
|
)
|
|
break;
|
|
}
|
|
|
|
// If no real VGA device was found and
|
|
// the VGA device isn't in use and
|
|
// this is not Personal Windows, choose
|
|
// any display to be marked as the VGA
|
|
if (PhysDispMarkVga == NULL &&
|
|
!VGAInUse &&
|
|
!((USER_SHARED_DATA->NtProductType == NtProductWinNt) &&
|
|
(USER_SHARED_DATA->SuiteMask & (1 << Personal))))
|
|
{
|
|
for (PhysDispMarkVga = gpGraphicsDeviceList;
|
|
PhysDispMarkVga != NULL;
|
|
PhysDispMarkVga = PhysDispMarkVga->pNextGraphicsDevice
|
|
)
|
|
{
|
|
if ((PhysDispMarkVga != gPhysDispVGA) &&
|
|
((PhysDispMarkVga->stateFlags & (DISPLAY_DEVICE_MIRRORING_DRIVER |
|
|
DISPLAY_DEVICE_DISCONNECT |
|
|
DISPLAY_DEVICE_REMOTE) ) == 0)
|
|
)
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
// No real VGA device was found,
|
|
// so we'll use VGA.sys device.
|
|
if (PhysDispMarkVga == NULL)
|
|
{
|
|
PhysDispMarkVga = gPhysDispVGA;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remove the VGA.sys device from the list of devices if it is a duplicate.
|
|
//
|
|
|
|
if (gPhysDispVGA != PhysDispMarkVga)
|
|
{
|
|
//
|
|
// Take this device out of the list.
|
|
//
|
|
// Handle the case where the device is already removed
|
|
//
|
|
|
|
if (gpGraphicsDeviceList == gPhysDispVGA)
|
|
{
|
|
gpGraphicsDeviceList = gPhysDispVGA->pNextGraphicsDevice;
|
|
}
|
|
else
|
|
{
|
|
PhysDispTmp = gpGraphicsDeviceList;
|
|
|
|
while (PhysDispTmp)
|
|
{
|
|
if (PhysDispTmp->pNextGraphicsDevice != gPhysDispVGA)
|
|
{
|
|
PhysDispTmp = PhysDispTmp->pNextGraphicsDevice;
|
|
continue;
|
|
}
|
|
|
|
PhysDispTmp->pNextGraphicsDevice = gPhysDispVGA->pNextGraphicsDevice;
|
|
|
|
if (PhysDispTmp->pNextGraphicsDevice == NULL)
|
|
{
|
|
gpGraphicsDeviceListLast = PhysDispTmp;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Mark the VGA as a VGA so we can do easy test later to match
|
|
// graphics devices.
|
|
//
|
|
|
|
gPhysDispVGA->pVgaDevice = gPhysDispVGA;
|
|
|
|
//
|
|
// If VGA device is in use, replace all ppdev
|
|
// references to it with the new device and
|
|
// update state on new device.
|
|
//
|
|
|
|
if (VGAInUse)
|
|
{
|
|
DWORD VGATransferFlags = (DISPLAY_DEVICE_ATTACHED_TO_DESKTOP |
|
|
DISPLAY_DEVICE_PRIMARY_DEVICE);
|
|
|
|
PhysDispMarkVga->stateFlags |= gPhysDispVGA->stateFlags & VGATransferFlags;
|
|
gPhysDispVGA->stateFlags &= ~VGATransferFlags;
|
|
|
|
for (ppdev = gppdevList; ppdev != NULL; ppdev = ppdev->ppdevNext)
|
|
{
|
|
if (ppdev->pGraphicsDevice == gPhysDispVGA)
|
|
{
|
|
ppdev->pGraphicsDevice = PhysDispMarkVga;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
GreReleaseSemaphoreEx(ghsemDriverMgmt);
|
|
}
|
|
|
|
//
|
|
// Save the VGA device
|
|
//
|
|
|
|
PhysDispMarkVga->pVgaDevice = PhysDispIsVga;
|
|
|
|
//
|
|
// Build the list of text modes for this device
|
|
//
|
|
|
|
TRACE_INIT(("Drv_Trace: LoadDriver: get text modes\n"));
|
|
|
|
|
|
|
|
Status = GreDeviceIoControl(PhysDispIsVga->pDeviceHandle,
|
|
IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES,
|
|
NULL,
|
|
0,
|
|
&NumModes,
|
|
NumModesLength,
|
|
&BytesReturned);
|
|
|
|
cbBuffer = NumModes.NumModes * NumModes.ModeInformationLength;
|
|
|
|
if ( (NT_SUCCESS(Status)) &&
|
|
(lpModes = (PVIDEO_MODE_INFORMATION)
|
|
PALLOCMEM(cbBuffer, GDITAG_DRVSUP)) )
|
|
{
|
|
Status = GreDeviceIoControl(PhysDispIsVga->pDeviceHandle,
|
|
IOCTL_VIDEO_QUERY_AVAIL_MODES,
|
|
NULL,
|
|
0,
|
|
lpModes,
|
|
cbBuffer,
|
|
&BytesReturned);
|
|
|
|
pVideoModeSave = lpModes;
|
|
|
|
//
|
|
// We will not support more than three text modes.
|
|
// So just allocate enough for that.
|
|
//
|
|
|
|
|
|
if ((NT_SUCCESS(Status)) &&
|
|
(pUsDevmode = (LPDEVMODEW)PALLOCMEM(3 * sizeof(DEVMODEW), GDITAG_DRVSUP)) &&
|
|
(pUsDevmodeMark = (LPDEVMODEMARK)PALLOCMEM(3 * sizeof(DEVMODEMARK), GDITAG_DRVSUP)) )
|
|
{
|
|
TRACE_INIT(("Drv_Trace: LoadDriver: parsing fullscreen modes\n"));
|
|
|
|
while (cbBuffer != 0)
|
|
{
|
|
if (lpModes->AttributeFlags & VIDEO_MODE_COLOR)
|
|
{
|
|
if (lpModes->AttributeFlags & VIDEO_MODE_GRAPHICS)
|
|
{
|
|
if (cGraphicsModes)
|
|
{
|
|
goto ConsoleNextMode;
|
|
}
|
|
|
|
ptmpDevmode = pUsDevmode + 2;
|
|
|
|
//
|
|
// Make sure we have only one graphics mode
|
|
//
|
|
|
|
if (IsNEC_98)
|
|
{
|
|
if ((lpModes->VisScreenWidth != 640) ||
|
|
(lpModes->VisScreenHeight != 480) ||
|
|
((lpModes->NumberOfPlanes *
|
|
lpModes->BitsPerPlane) != 8))
|
|
{
|
|
goto ConsoleNextMode;
|
|
}
|
|
}
|
|
else if ((lpModes->VisScreenWidth != 640) ||
|
|
(lpModes->VisScreenHeight != 480) ||
|
|
((lpModes->NumberOfPlanes *
|
|
lpModes->BitsPerPlane) != 4))
|
|
{
|
|
goto ConsoleNextMode;
|
|
}
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvInitConsole: VGA graphics mode\n"));
|
|
cGraphicsModes++;
|
|
}
|
|
else
|
|
{
|
|
ptmpDevmode = pUsDevmode + cTextModes;
|
|
|
|
//
|
|
// Make sure we have only 2 text modes
|
|
//
|
|
|
|
if (cTextModes == 2)
|
|
{
|
|
RIP("Drv_Trace: VGA compatible device has too many text modes\n");
|
|
|
|
goto ConsoleNextMode;
|
|
}
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvInitConsole: VGA text mode\n"));
|
|
cTextModes++;
|
|
}
|
|
|
|
RtlZeroMemory(ptmpDevmode, sizeof(DEVMODEW));
|
|
|
|
if (!(lpModes->AttributeFlags & VIDEO_MODE_GRAPHICS))
|
|
{
|
|
ptmpDevmode->dmDisplayFlags = DMDISPLAYFLAGS_TEXTMODE;
|
|
}
|
|
|
|
memcpy(ptmpDevmode->dmDeviceName,
|
|
L"FULLSCREEN CONSOLE",
|
|
sizeof(L"FULLSCREEN CONSOLE"));
|
|
|
|
ptmpDevmode->dmSize = sizeof(DEVMODEW);
|
|
ptmpDevmode->dmSpecVersion = DM_SPECVERSION;
|
|
ptmpDevmode->dmDriverVersion = DM_SPECVERSION;
|
|
|
|
ptmpDevmode->dmPelsWidth =
|
|
lpModes->VisScreenWidth;
|
|
ptmpDevmode->dmPelsHeight =
|
|
lpModes->VisScreenHeight;
|
|
ptmpDevmode->dmBitsPerPel =
|
|
lpModes->NumberOfPlanes *
|
|
lpModes->BitsPerPlane;
|
|
ptmpDevmode->dmDisplayOrientation = DMDO_DEFAULT;
|
|
ptmpDevmode->dmDisplayFixedOutput = DMDFO_DEFAULT;
|
|
|
|
ptmpDevmode->dmFields = DM_BITSPERPEL |
|
|
DM_PELSWIDTH |
|
|
DM_PELSHEIGHT |
|
|
DM_DISPLAYFREQUENCY |
|
|
DM_DISPLAYFLAGS |
|
|
DM_DISPLAYORIENTATION;
|
|
|
|
//
|
|
// NOTE !!!
|
|
// As a hack, lets store the mode number in
|
|
// a field we don't use
|
|
//
|
|
|
|
ptmpDevmode->dmOrientation =
|
|
(USHORT) lpModes->ModeIndex;
|
|
}
|
|
|
|
ConsoleNextMode:
|
|
cbBuffer -= NumModes.ModeInformationLength;
|
|
lpModes = (PVIDEO_MODE_INFORMATION)
|
|
(((PUCHAR)lpModes) + NumModes.ModeInformationLength);
|
|
}
|
|
}
|
|
|
|
VFREEMEM(pVideoModeSave);
|
|
}
|
|
|
|
//
|
|
// if everything went OK with that, then we can save this
|
|
// device as vga compatible !
|
|
//
|
|
// If no modes are available, do not setup this device.
|
|
// Otherwise, EnumDisplaySettings will end up trying to get
|
|
// the list of modes for this device, which it can not do.
|
|
//
|
|
|
|
|
|
if ((cGraphicsModes == 1) &&
|
|
(cTextModes == 2))
|
|
{
|
|
UNICODE_STRING DeviceName;
|
|
HANDLE pDeviceHandle;
|
|
PVOID pFileObject;
|
|
|
|
TRACE_INIT(("Drv_Trace: LoadDriver: saving VGA compatible device\n"));
|
|
|
|
//
|
|
// Copy the string and the handle ...
|
|
//
|
|
|
|
RtlCopyMemory(&gFullscreenGraphicsDevice,
|
|
PhysDispIsVga,
|
|
sizeof(GRAPHICS_DEVICE));
|
|
|
|
//
|
|
// 2 text modes + 1 graphics mode.
|
|
//
|
|
|
|
gFullscreenGraphicsDevice.cbdevmodeInfo = 3 * sizeof(DEVMODEW);
|
|
gFullscreenGraphicsDevice.devmodeInfo = pUsDevmode;
|
|
gFullscreenGraphicsDevice.numRawModes = 3;
|
|
gFullscreenGraphicsDevice.devmodeMarks = pUsDevmodeMark;
|
|
for (ULONG i = 0; i < 3; i++)
|
|
{
|
|
pUsDevmodeMark[i].bPruned = 0;
|
|
pUsDevmodeMark[i].pDevMode = &pUsDevmode[i];
|
|
}
|
|
|
|
//
|
|
// Write the name of the fullscreen device in the registry.
|
|
//
|
|
|
|
RtlInitUnicodeString(&DeviceName,
|
|
gFullscreenGraphicsDevice.szNtDeviceName);
|
|
|
|
RtlWriteRegistryValue(RTL_REGISTRY_DEVICEMAP,
|
|
L"Video",
|
|
L"VgaCompatible",
|
|
REG_SZ,
|
|
DeviceName.Buffer,
|
|
DeviceName.Length + sizeof(UNICODE_NULL));
|
|
|
|
//
|
|
// Now create the FE fullscreen device.
|
|
//
|
|
|
|
RtlInitUnicodeString(&DeviceName, DD_FULLSCREEN_VIDEO_DEVICE_NAME);
|
|
|
|
Status = IoGetDeviceObjectPointer(&DeviceName,
|
|
(ACCESS_MASK) (0),
|
|
(PFILE_OBJECT *) &pFileObject,
|
|
(PDEVICE_OBJECT *) &pDeviceHandle);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
gFeFullscreenGraphicsDevice.hkClassDriverConfig = 0;
|
|
gFeFullscreenGraphicsDevice.pDeviceHandle = pDeviceHandle;
|
|
|
|
RtlCopyMemory(&gFeFullscreenGraphicsDevice,
|
|
DD_FULLSCREEN_VIDEO_DEVICE_NAME,
|
|
sizeof(DD_FULLSCREEN_VIDEO_DEVICE_NAME));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make VGA the head of list, so that VGA always get processed first upon mode change
|
|
// And make it "\\.\Display1" for compatibility purpose
|
|
//
|
|
if (PhysDispMarkVga != gpGraphicsDeviceList)
|
|
{
|
|
for (PhysDispTmp = gpGraphicsDeviceList;
|
|
PhysDispTmp != NULL;
|
|
PhysDispTmp = PhysDispTmp->pNextGraphicsDevice)
|
|
{
|
|
if (PhysDispTmp->pNextGraphicsDevice == PhysDispMarkVga)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERTGDI (PhysDispTmp != NULL, "VGA device should alway be in gpGraphicsDeviceList chain\n");
|
|
|
|
PhysDispTmp->pNextGraphicsDevice = PhysDispMarkVga->pNextGraphicsDevice;
|
|
PhysDispMarkVga->pNextGraphicsDevice = gpGraphicsDeviceList;
|
|
gpGraphicsDeviceList = PhysDispMarkVga;
|
|
if (gpGraphicsDeviceListLast == PhysDispMarkVga)
|
|
gpGraphicsDeviceListLast = PhysDispTmp;
|
|
|
|
}
|
|
|
|
if (wcscmp(gpGraphicsDeviceList->szWinDeviceName, L"\\\\.\\DISPLAY1") != 0)
|
|
{
|
|
for (PhysDispTmp = gpGraphicsDeviceList;
|
|
PhysDispTmp != NULL;
|
|
PhysDispTmp = PhysDispTmp->pNextGraphicsDevice)
|
|
{
|
|
if (wcscmp(PhysDispTmp->szWinDeviceName, L"\\\\.\\DISPLAY1") == 0)
|
|
{
|
|
wcscpy(PhysDispTmp->szWinDeviceName, gpGraphicsDeviceList->szWinDeviceName);
|
|
}
|
|
}
|
|
wcscpy(gpGraphicsDeviceList->szWinDeviceName, L"\\\\.\\DISPLAY1");
|
|
}
|
|
|
|
return PhysDispMarkVga;
|
|
}
|
|
|
|
|
|
#ifdef IOCTL_VIDEO_USE_DEVICE_IN_SESSION
|
|
|
|
BOOL
|
|
bSetDeviceSessionUsage(
|
|
PGRAPHICS_DEVICE PhysDisp,
|
|
BOOL bEnable
|
|
)
|
|
{
|
|
GDIFunctionID(bSetDeviceSessionUsage);
|
|
|
|
ASSERTGDI(PhysDisp != NULL, "NULL Graphics Device\n");
|
|
|
|
BOOL bRet;
|
|
|
|
// Virtual devices (meta, mirror, remote, and disconnect) have the
|
|
// capability of being used in multiple sessions since there is no
|
|
// physical device.
|
|
// NOTE: What are the implications for mirroring drivers?
|
|
if (PhysDisp != (PGRAPHICS_DEVICE) DDML_DRIVER &&
|
|
!(PhysDisp->stateFlags & (DISPLAY_DEVICE_MIRRORING_DRIVER |
|
|
DISPLAY_DEVICE_REMOTE |
|
|
DISPLAY_DEVICE_DISCONNECT)))
|
|
{
|
|
NTSTATUS Status;
|
|
DWORD dwBytesReturned;
|
|
VIDEO_DEVICE_SESSION_STATUS vdSessionStatus = { bEnable, FALSE };
|
|
|
|
ASSERTGDI(PhysDisp->pDeviceHandle != NULL, "Physical device has NULL handle.");
|
|
|
|
Status = GreDeviceIoControl(PhysDisp->pDeviceHandle,
|
|
IOCTL_VIDEO_USE_DEVICE_IN_SESSION,
|
|
&vdSessionStatus,
|
|
sizeof(vdSessionStatus),
|
|
&vdSessionStatus,
|
|
sizeof(vdSessionStatus),
|
|
&dwBytesReturned);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
if (!vdSessionStatus.bSuccess)
|
|
{
|
|
if (bEnable)
|
|
{
|
|
DbgPrint("Trying to enable physical device already in use.\n");
|
|
}
|
|
else
|
|
{
|
|
DbgPrint("Trying to disable physical device not enabled in this session.\n");
|
|
}
|
|
}
|
|
|
|
bRet = vdSessionStatus.bSuccess;
|
|
}
|
|
else
|
|
{
|
|
WARNING("IOCTL_VIDEO_USE_DEVICE_IN_SESSION requested failed.");
|
|
bRet = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bRet = TRUE;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
#endif IOCTL_VIDEO_USE_DEVICE_IN_SESSION
|
|
|
|
|
|
extern "C"
|
|
VOID
|
|
CloseLocalGraphicsDevices()
|
|
{
|
|
|
|
PGRAPHICS_DEVICE PhysDisp = NULL;
|
|
|
|
TRACE_INIT(("CloseLocalGraphicsDevices\n"));
|
|
|
|
if (gpLocalGraphicsDeviceList == NULL) {
|
|
TRACE_INIT(("CloseLocalGraphicsDevices - gpLocalGraphicsDeviceList is not st yet\n"));
|
|
return;
|
|
}
|
|
|
|
for (PhysDisp = gpLocalGraphicsDeviceList;
|
|
PhysDisp != NULL;
|
|
PhysDisp = PhysDisp->pNextGraphicsDevice)
|
|
{
|
|
if (PhysDisp->pFileObject != NULL) {
|
|
TRACE_INIT(("CloseLocalGraphicsDevices, Closing : %ws\n", PhysDisp->szNtDeviceName));
|
|
ObDereferenceObject(PhysDisp->pFileObject);
|
|
PhysDisp->pDeviceHandle = NULL;
|
|
PhysDisp->pFileObject = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
// If there is a separate VGA device close it too
|
|
|
|
if ( (gPhysDispVGA != NULL) && (gPhysDispVGA->pFileObject != NULL) ) {
|
|
ObDereferenceObject(gPhysDispVGA->pFileObject);
|
|
gPhysDispVGA->pDeviceHandle = NULL;
|
|
gPhysDispVGA->pFileObject = NULL;
|
|
}
|
|
}
|
|
|
|
extern "C"
|
|
VOID
|
|
OpenLocalGraphicsDevices()
|
|
{
|
|
|
|
UNICODE_STRING DeviceName;
|
|
PGRAPHICS_DEVICE PhysDisp = NULL;
|
|
NTSTATUS status;
|
|
BOOL bVgaDeviceInList = FALSE;
|
|
|
|
TRACE_INIT(("OpenLocalGraphicsDevices\n"));
|
|
|
|
if (gpLocalGraphicsDeviceList == NULL) {
|
|
TRACE_INIT(("OpenLocalGraphicsDevices - gpLocalGraphicsDeviceList is not st yet\n"));
|
|
return;
|
|
}
|
|
|
|
for (PhysDisp = gpLocalGraphicsDeviceList;
|
|
PhysDisp != NULL;
|
|
PhysDisp = PhysDisp->pNextGraphicsDevice)
|
|
{
|
|
if (PhysDisp->pFileObject == NULL) {
|
|
|
|
TRACE_INIT(("OpenLocalGraphicsDevices opening : %ws\n", PhysDisp->szNtDeviceName));
|
|
RtlInitUnicodeString(&DeviceName, PhysDisp->szNtDeviceName);
|
|
status = IoGetDeviceObjectPointer(&DeviceName,
|
|
(ACCESS_MASK) (0),
|
|
(PFILE_OBJECT *) &PhysDisp->pFileObject,
|
|
(PDEVICE_OBJECT *)&PhysDisp->pDeviceHandle);
|
|
if (PhysDisp == gPhysDispVGA ) {
|
|
bVgaDeviceInList = TRUE;
|
|
}
|
|
if (!NT_SUCCESS(status)) {
|
|
TRACE_INIT(("OpenLocalGraphicsDevices failed with status %0x\n", status));
|
|
} else{
|
|
TRACE_INIT(("OpenLocalGraphicsDevices OK\n"));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// If there is a separate VGA device, Open it too
|
|
|
|
if (!bVgaDeviceInList && (gPhysDispVGA != NULL)) {
|
|
RtlInitUnicodeString(&DeviceName, gPhysDispVGA->szNtDeviceName);
|
|
status = IoGetDeviceObjectPointer(&DeviceName,
|
|
(ACCESS_MASK) (0),
|
|
(PFILE_OBJECT *) &gPhysDispVGA->pFileObject,
|
|
(PDEVICE_OBJECT *)&gPhysDispVGA->pDeviceHandle);
|
|
if (!NT_SUCCESS(status)) {
|
|
TRACE_INIT(("OpenLocalGraphicsDevices failed to ope VGA device with status %0x\n", status));
|
|
} else{
|
|
TRACE_INIT(("OpenLocalGraphicsDevices, open VGA device OK\n"));
|
|
}
|
|
}
|
|
}
|
|
|
|
extern "C"
|
|
BOOL
|
|
DrvSetGraphicsDevices(PWSTR pDisplayDriverName)
|
|
{
|
|
|
|
BOOL bLocal;
|
|
|
|
|
|
if (gProtocolType == PROTOCOL_CONSOLE) {
|
|
bLocal = TRUE;
|
|
} else {
|
|
bLocal = FALSE;
|
|
}
|
|
wcscpy(G_DisplayDriverNames, pDisplayDriverName);
|
|
|
|
return (DrvUpdateGraphicsDeviceList(TRUE,FALSE,bLocal));
|
|
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* DrvUpdateGraphicsDeviceList
|
|
*
|
|
* Update the list of devices in the PhysDisp linked list.
|
|
*
|
|
* This function return TRUE for SUCCESS
|
|
* If the functions returns FALSE, the it means the active HDEV should be
|
|
* disabled, and we should get called back With the parameter being set to TRUE.
|
|
*
|
|
* 09-Oct-1996 andreva created
|
|
* Updates the local graphics device list or the remote graphics device list.
|
|
* This function is called on InitVideo but also by xxxRemoteReconnect since
|
|
* At reconnect the list may change ( case of reconnecting a session with a
|
|
* client from a different protocol or case of reconnecting the console session
|
|
* to a remote client or reconnecting an inialy remote session to the local
|
|
* console video.
|
|
\**************************************************************************/
|
|
|
|
BOOL
|
|
DrvUpdateGraphicsDeviceList(
|
|
BOOL bDefaultDisplayDisabled,
|
|
BOOL bReenumerationNeeded,
|
|
BOOL bLocal)
|
|
{
|
|
ULONG deviceNumber = 0;
|
|
BOOL newDevice = FALSE;
|
|
WCHAR devName[32];
|
|
UNICODE_STRING DeviceName;
|
|
PDEVICE_OBJECT pDeviceHandle = NULL;
|
|
PVOID pFileObject;
|
|
|
|
PWSTR *SymbolicLinkList;
|
|
BOOL bGotMonitorPdos;
|
|
PDEVICE_RELATIONS pDeviceRelations;
|
|
|
|
NTSTATUS status;
|
|
PGRAPHICS_DEVICE PhysDisp = NULL;
|
|
HANDLE hkRegistry = NULL;
|
|
|
|
VIDEO_WIN32K_CALLBACKS videoCallback;
|
|
ULONG bytesReturned;
|
|
BOOL bReturn = TRUE;
|
|
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvUpdateGraphicsDeviceList: Enter\n"));
|
|
|
|
//
|
|
// ISSUE - we don't handle deletions !!!
|
|
//
|
|
|
|
//
|
|
// If the maximum value has increased for video device objects, then
|
|
// go open the new ones.
|
|
//
|
|
if ( bLocal ) {
|
|
gcNextGlobalDeviceNumber = gcLocalNextGlobalDeviceNumber;
|
|
gpGraphicsDeviceList = gpLocalGraphicsDeviceList;
|
|
gpGraphicsDeviceListLast =gpLocalGraphicsDeviceListLast;
|
|
gcNextGlobalPhysicalOutputNumber = gcLocalNextGlobalPhysicalOutputNumber;
|
|
gcNextGlobalVirtualOutputNumber = gcLocalNextGlobalVirtualOutputNumber;
|
|
ULONG defaultValue = 0;
|
|
|
|
RTL_QUERY_REGISTRY_TABLE QueryTable[] = {
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT, L"MaxObjectNumber",
|
|
&deviceNumber, REG_DWORD, &defaultValue, 4},
|
|
{NULL, 0, NULL}
|
|
};
|
|
|
|
RtlQueryRegistryValues(RTL_REGISTRY_DEVICEMAP,
|
|
L"VIDEO",
|
|
&QueryTable[0],
|
|
NULL,
|
|
NULL);
|
|
} else{
|
|
gcNextGlobalDeviceNumber = gcRemoteNextGlobalDeviceNumber;
|
|
gpGraphicsDeviceList = gpRemoteGraphicsDeviceList;
|
|
gpGraphicsDeviceListLast =gpRemoteGraphicsDeviceListLast;
|
|
gcNextGlobalPhysicalOutputNumber = gcRemoteNextGlobalPhysicalOutputNumber;
|
|
gcNextGlobalVirtualOutputNumber = gcRemoteNextGlobalVirtualOutputNumber;
|
|
|
|
//
|
|
// If we don't yet have this protocol in our remote device list,
|
|
// Set deviceNumber in a way that we'll iterate once to create
|
|
// a GRAPHICS_DEVICE for it, otherwise set to zero so that we don't
|
|
// create a new GRAPHICS_DEVICE.
|
|
//
|
|
if (gProtocolType != PROTOCOL_DISCONNECT && !DrvIsProtocolAlreadyKnown()) {
|
|
deviceNumber = gcRemoteNextGlobalDeviceNumber;
|
|
}
|
|
}
|
|
|
|
|
|
// IoGetDeviceInterfaces(GUID_DISPLAY_INTERFACE_STANDARD,
|
|
// NULL,
|
|
|
|
// 0,
|
|
// &SymbolicLinkList);
|
|
|
|
|
|
while (gProtocolType != PROTOCOL_DISCONNECT && gcNextGlobalDeviceNumber <= deviceNumber)
|
|
{
|
|
TRACE_INIT(("Drv_Trace:DrvUpdateGraphicsDeviceList: Device %d\n", gcNextGlobalDeviceNumber));
|
|
|
|
if (bDefaultDisplayDisabled == FALSE) {
|
|
|
|
TRACE_INIT(("Drv_Trace:DrvUpdateGraphicsDeviceList: Exit RETRY\n\n"));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
newDevice = TRUE;
|
|
|
|
swprintf(devName,
|
|
L"\\Device\\Video%d",
|
|
gcNextGlobalDeviceNumber);
|
|
|
|
RtlInitUnicodeString(&DeviceName, devName);
|
|
|
|
TRACE_INIT(("Drv_Trace: \tNewDevice: Try creating device %ws\n",
|
|
devName));
|
|
|
|
if ( !bLocal )
|
|
{
|
|
//
|
|
// FOR HYDRA
|
|
//
|
|
|
|
pFileObject = (PFILE_OBJECT)G_RemoteVideoFileObject;
|
|
pDeviceHandle = IoGetRelatedDeviceObject ((PFILE_OBJECT)pFileObject);
|
|
|
|
if (pDeviceHandle) {
|
|
status = STATUS_SUCCESS;
|
|
} else {
|
|
status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
//deviceNumber = 0; //Only one display driver for remote sessions
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Opening a new device will cause the Initialize
|
|
// routine of a miniport driver to be called.
|
|
// This may cause the driver to change some state, which could
|
|
// affect the state of another driver on the same device
|
|
// (opening the weitek driver if the vga is running.
|
|
//
|
|
// For that reason, the other device should be temporarily
|
|
// closed down when we do the create, and then reinitialized
|
|
// afterwards.
|
|
//
|
|
// Handle special case when we are opening initial device and
|
|
// gpDispInfo->hDev does not exist yet.
|
|
//
|
|
|
|
status = IoGetDeviceObjectPointer(&DeviceName,
|
|
(ACCESS_MASK) (0),
|
|
(PFILE_OBJECT *) &pFileObject,
|
|
&pDeviceHandle);
|
|
}
|
|
|
|
//
|
|
// if we got a configuration error from the device (HwInitialize
|
|
// routine failed, then just go on to the next device.
|
|
//
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TRACE_INIT(("Drv_Trace: \tNewDevice: No such device - Status is %0x\n",status));
|
|
|
|
//
|
|
// For remote devices, we don't want to increment the Next device number
|
|
// if we are not creating the device. The reason is that remote devices
|
|
// do not have a fixed number and we want to use a device number only
|
|
// if we are really creating a remote device for it.
|
|
|
|
if (bLocal) {
|
|
gcNextGlobalDeviceNumber++;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate a buffer if necessary:
|
|
//
|
|
|
|
if (PhysDisp == NULL)
|
|
{
|
|
PhysDisp = (PGRAPHICS_DEVICE) PALLOCMEM(sizeof(GRAPHICS_DEVICE),
|
|
GDITAG_GDEVICE);
|
|
}
|
|
|
|
if (PhysDisp)
|
|
{
|
|
PhysDisp->numMonitorDevice = 0;
|
|
PhysDisp->MonitorDevices = NULL;
|
|
PhysDisp->pDeviceHandle = (HANDLE) pDeviceHandle;
|
|
PhysDisp->ProtocolType = gProtocolType;
|
|
if (!bLocal) {
|
|
PhysDisp->stateFlags |= DISPLAY_DEVICE_REMOTE;
|
|
}
|
|
|
|
if (bLocal) {
|
|
PhysDisp->pFileObject = pFileObject;
|
|
} else {
|
|
PhysDisp->pFileObject = NULL;
|
|
}
|
|
|
|
bGotMonitorPdos = FALSE;
|
|
|
|
if (bLocal)
|
|
{
|
|
//
|
|
// Tell the video port driver about us so we can
|
|
// do power management notifucations
|
|
//
|
|
RtlZeroMemory(&videoCallback, sizeof(VIDEO_WIN32K_CALLBACKS));
|
|
videoCallback.PhysDisp = PhysDisp;
|
|
videoCallback.Callout = VideoPortCallout;
|
|
|
|
status = GreDeviceIoControl(PhysDisp->pDeviceHandle,
|
|
IOCTL_VIDEO_INIT_WIN32K_CALLBACKS,
|
|
&videoCallback,
|
|
sizeof(VIDEO_WIN32K_CALLBACKS),
|
|
&videoCallback,
|
|
sizeof(VIDEO_WIN32K_CALLBACKS),
|
|
&bytesReturned);
|
|
|
|
if (videoCallback.bACPI)
|
|
{
|
|
PhysDisp->stateFlags |= DISPLAY_DEVICE_ACPI;
|
|
}
|
|
if (videoCallback.DualviewFlags & VIDEO_DUALVIEW_REMOVABLE)
|
|
{
|
|
PhysDisp->stateFlags |= DISPLAY_DEVICE_REMOVABLE;
|
|
}
|
|
if (videoCallback.DualviewFlags & (VIDEO_DUALVIEW_PRIMARY | VIDEO_DUALVIEW_SECONDARY))
|
|
{
|
|
PhysDisp->stateFlags |= DISPLAY_DEVICE_DUALVIEW;
|
|
}
|
|
|
|
PhysDisp->pPhysDeviceHandle = videoCallback.pPhysDeviceObject;
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
}
|
|
|
|
//
|
|
// For a PnP Driver, get the PDO so we can enumerate the monitors
|
|
//
|
|
|
|
status = DrvSendPnPIrp(pDeviceHandle,
|
|
TargetDeviceRelation,
|
|
&pDeviceRelations);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
PDEVICE_OBJECT pdoVideoChip = pDeviceRelations->Objects[0];
|
|
PDEVICE_OBJECT pdoChild;
|
|
PDEVICE_OBJECT *pActivePdos, *pPdos;
|
|
ULONG_PTR cCount = 0;
|
|
ULONG i;
|
|
WCHAR className[8];
|
|
NTSTATUS status2;
|
|
|
|
ASSERTGDI(pDeviceRelations->Count == 1,
|
|
"TargetDeviceRelation should only get one PDO\n");
|
|
|
|
//
|
|
// Note: free with ExFreePool rather than GdiFreePool
|
|
// because pDeviceRelations is allocated by IoAllocateIrp
|
|
// (ntos\io\iosubs.c) in DrvSendPnPIrp.
|
|
//
|
|
|
|
ExFreePool(pDeviceRelations);
|
|
|
|
//
|
|
// In 5.0, the driver configuration data is stored under
|
|
// Class\GUID\xxx
|
|
//
|
|
TRACE_INIT(("Drv_Trace: \tNewDevice: Get device Configuration from Class Key\n"));
|
|
|
|
status = IoOpenDeviceRegistryKey(pdoVideoChip,
|
|
PLUGPLAY_REGKEY_DRIVER,
|
|
MAXIMUM_ALLOWED,
|
|
&hkRegistry);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
PVIDEO_MONITOR_DEVICE pMonitorDevices = NULL;
|
|
ULONG numMonitorDevice = 0;
|
|
|
|
//
|
|
// Force all the monitors on this device to be enumerated.
|
|
//
|
|
|
|
if (bReenumerationNeeded) {
|
|
IoSynchronousInvalidateDeviceRelations(pdoVideoChip, BusRelations);
|
|
}
|
|
|
|
if (NT_SUCCESS(GreDeviceIoControl(pDeviceHandle,
|
|
IOCTL_VIDEO_ENUM_MONITOR_PDO,
|
|
NULL,
|
|
0,
|
|
&pMonitorDevices,
|
|
sizeof(PVOID),
|
|
&bytesReturned))
|
|
&& pMonitorDevices != NULL)
|
|
{
|
|
bGotMonitorPdos = TRUE;
|
|
|
|
//
|
|
// Get the number of PDOs
|
|
//
|
|
numMonitorDevice = 0;
|
|
while (pMonitorDevices[numMonitorDevice].pdo != NULL)
|
|
{
|
|
ObDereferenceObject(pMonitorDevices[numMonitorDevice].pdo);
|
|
numMonitorDevice++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create the standard graphics device
|
|
//
|
|
TRACE_INIT(("Drv_Trace: \tNewDevice: Get display Configuration from Class Key\n"));
|
|
|
|
DrvGetDeviceConfigurationInformation(PhysDisp, hkRegistry, TRUE);
|
|
|
|
//
|
|
// Per Monitor Settings
|
|
//
|
|
if (bGotMonitorPdos && numMonitorDevice)
|
|
{
|
|
PhysDisp->numMonitorDevice = numMonitorDevice;
|
|
PhysDisp->MonitorDevices = (PVIDEO_MONITOR_DEVICE)
|
|
PALLOCMEM(sizeof(VIDEO_MONITOR_DEVICE) * numMonitorDevice,
|
|
GDITAG_GDEVICE);
|
|
for (cCount = 0; cCount < numMonitorDevice; cCount++)
|
|
{
|
|
PhysDisp->MonitorDevices[cCount].flag = 0;
|
|
if (pMonitorDevices[cCount].flag & VIDEO_CHILD_ACTIVE) {
|
|
PhysDisp->MonitorDevices[cCount].flag |= DISPLAY_DEVICE_ACTIVE;
|
|
}
|
|
if ((pMonitorDevices[cCount].flag & VIDEO_CHILD_DETACHED) == 0) {
|
|
PhysDisp->MonitorDevices[cCount].flag |= DISPLAY_DEVICE_ATTACHED;
|
|
}
|
|
if ((pMonitorDevices[cCount].flag & VIDEO_CHILD_NOPRUNE_FREQ) == 0) {
|
|
PhysDisp->MonitorDevices[cCount].flag |= DISPLAY_DEVICE_PRUNE_FREQ;
|
|
}
|
|
if ((pMonitorDevices[cCount].flag & VIDEO_CHILD_NOPRUNE_RESOLUTION) == 0) {
|
|
PhysDisp->MonitorDevices[cCount].flag |= DISPLAY_DEVICE_PRUNE_RESOLUTION;
|
|
}
|
|
PhysDisp->MonitorDevices[cCount].pdo = pMonitorDevices[cCount].pdo;
|
|
PhysDisp->MonitorDevices[cCount].HwID = pMonitorDevices[cCount].HwID;
|
|
}
|
|
}
|
|
|
|
if (bGotMonitorPdos)
|
|
{
|
|
ExFreePool(pMonitorDevices);
|
|
}
|
|
|
|
ZwCloseKey(hkRegistry);
|
|
}
|
|
|
|
ObDereferenceObject(pdoVideoChip);
|
|
}
|
|
else if ((PhysDisp->stateFlags & DISPLAY_DEVICE_DUALVIEW) &&
|
|
PhysDisp->pPhysDeviceHandle )
|
|
{
|
|
//
|
|
// For Dualview secondary, there is no real PDO
|
|
//
|
|
status = IoOpenDeviceRegistryKey((PDEVICE_OBJECT)PhysDisp->pPhysDeviceHandle,
|
|
PLUGPLAY_REGKEY_DRIVER,
|
|
MAXIMUM_ALLOWED,
|
|
&hkRegistry);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
DrvGetDeviceConfigurationInformation(PhysDisp, hkRegistry, TRUE);
|
|
ZwCloseKey(hkRegistry);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Do this here as this field must be initialized for the next
|
|
// function call.
|
|
//
|
|
|
|
swprintf(&(PhysDisp->szNtDeviceName[0]),
|
|
L"\\Device\\Video%d",
|
|
gcNextGlobalDeviceNumber++);
|
|
|
|
//
|
|
// If the software key information could not be obtained,
|
|
// try the legacy location
|
|
// In 4.0, the driver configuration data was located in
|
|
// <services>\DeviceX
|
|
//
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TRACE_INIT(("Drv_Trace: \tNewDevice: Failed to get PDO device Configuration info\n"));
|
|
|
|
hkRegistry = DrvGetRegistryHandleFromDeviceMap(
|
|
PhysDisp,
|
|
DispDriverRegGlobal,
|
|
NULL,
|
|
NULL,
|
|
&status,
|
|
gProtocolType);
|
|
|
|
//
|
|
// Start at entry 2 in the list because we want to skip the
|
|
// subdirectory
|
|
//
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
DrvGetDeviceConfigurationInformation(PhysDisp, hkRegistry, FALSE);
|
|
ZwCloseKey(hkRegistry);
|
|
}
|
|
}
|
|
|
|
//
|
|
// VGA driver and other old, MS detected drivers may have no
|
|
// description string. Add *something*.
|
|
//
|
|
|
|
if ((NT_SUCCESS(status)) &&
|
|
(PhysDisp->DeviceDescription == NULL))
|
|
{
|
|
if (PhysDisp->DeviceDescription = (LPWSTR) PALLOCNOZ(32, GDITAG_DRVSUP))
|
|
{
|
|
hkRegistry = DrvGetRegistryHandleFromDeviceMap(
|
|
PhysDisp,
|
|
DispDriverRegGlobal,
|
|
NULL,
|
|
PhysDisp->DeviceDescription,
|
|
&status,
|
|
gProtocolType);
|
|
if (hkRegistry)
|
|
{
|
|
ZwCloseKey(hkRegistry);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
if (PhysDisp->DeviceDescription == NULL) {
|
|
WARNING("\nDisplay Device Description is NULL!\n");
|
|
}
|
|
|
|
|
|
//
|
|
// If the device exists, keep it open and try the next one.
|
|
// Here we give Mirroring driver different names. Many applications assumes
|
|
// "\\.\Display1" as their primary display. But sometimes, mirroring driver
|
|
// gets loaded first, thus makes the assumption go complete bogus.
|
|
//
|
|
|
|
if (PhysDisp->stateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER)
|
|
{
|
|
swprintf(&(PhysDisp->szWinDeviceName[0]),
|
|
L"\\\\.\\DISPLAYV%d",
|
|
gcNextGlobalVirtualOutputNumber++);
|
|
}
|
|
else
|
|
{
|
|
swprintf(&(PhysDisp->szWinDeviceName[0]),
|
|
L"\\\\.\\DISPLAY%d",
|
|
gcNextGlobalPhysicalOutputNumber++);
|
|
}
|
|
|
|
//
|
|
// Link the new device at the end so we can enumerate
|
|
// in the right order. For remote devices, link the
|
|
// device to the list only if everything worked fine.
|
|
//
|
|
if (bLocal || NT_SUCCESS(status)) {
|
|
if (gpGraphicsDeviceList == NULL)
|
|
{
|
|
gpGraphicsDeviceList = PhysDisp;
|
|
gpGraphicsDeviceListLast = PhysDisp;
|
|
}
|
|
else
|
|
{
|
|
gpGraphicsDeviceListLast->pNextGraphicsDevice = PhysDisp;
|
|
gpGraphicsDeviceListLast = PhysDisp;
|
|
}
|
|
//
|
|
// We have successfully installed a GRAPHICS_DEVICE for a new
|
|
// remote protocol : increment known protocols count.
|
|
//
|
|
if (!bLocal) {
|
|
gcRemoteNextGlobalDeviceNumber++;
|
|
}
|
|
} else {
|
|
DrvCleanupOneGraphicsDevice(PhysDisp);
|
|
gcNextGlobalPhysicalOutputNumber--;
|
|
PhysDisp = NULL;
|
|
bReturn = FALSE;
|
|
}
|
|
|
|
PhysDisp = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the VGA device on the console.
|
|
//
|
|
|
|
if (bLocal && newDevice)
|
|
{
|
|
PGRAPHICS_DEVICE PhysDispVGA = DrvUpdateVgaDevice();
|
|
|
|
if (gbBaseVideo)
|
|
{
|
|
DrvSetSingleDisplay(PhysDispVGA);
|
|
}
|
|
}
|
|
|
|
|
|
// Create the entry for the Disconnect graphics device
|
|
if (DrvSetDisconnectedGraphicsDevice(bLocal)) {
|
|
TRACE_INIT(("DrvSetDisconnectedGraphicsDevice succeeded!\n"));
|
|
} else{
|
|
TRACE_INIT(("DrvSetDisconnectedGraphicsDevice Failed!\n"));
|
|
}
|
|
|
|
// write any change back to device lists
|
|
|
|
if ( bLocal ) {
|
|
gcLocalNextGlobalDeviceNumber = gcNextGlobalDeviceNumber;
|
|
gpLocalGraphicsDeviceList = gpGraphicsDeviceList;
|
|
gpLocalGraphicsDeviceListLast = gpGraphicsDeviceListLast;
|
|
gcLocalNextGlobalPhysicalOutputNumber = gcNextGlobalPhysicalOutputNumber;
|
|
gcLocalNextGlobalVirtualOutputNumber = gcNextGlobalVirtualOutputNumber;
|
|
} else{
|
|
//gcRemoteNextGlobalDeviceNumber = gcNextGlobalDeviceNumber;
|
|
gpRemoteGraphicsDeviceList = gpGraphicsDeviceList;
|
|
gpRemoteGraphicsDeviceListLast = gpGraphicsDeviceListLast;
|
|
gcRemoteNextGlobalPhysicalOutputNumber = gcNextGlobalPhysicalOutputNumber;
|
|
gcRemoteNextGlobalVirtualOutputNumber = gcNextGlobalVirtualOutputNumber;
|
|
}
|
|
|
|
TRACE_INIT(("DrvUpdateGraphicsDeviceList - gpGraphicsDeviceList : %x\n",gpGraphicsDeviceList));
|
|
TRACE_INIT(("DrvUpdateGraphicsDeviceList - gpLocalGraphicsDeviceList : %x\n",gpLocalGraphicsDeviceList));
|
|
TRACE_INIT(("DrvUpdateGraphicsDeviceList - gpRemoteGraphicsDeviceList : %x\n",gpRemoteGraphicsDeviceList));
|
|
TRACE_INIT(("Drv_Trace:DrvUpdateGraphicsDeviceList: Exit\n\n"));
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrvSetDisconnectedGraphicsDevice
|
|
* Creates an Entry for the disconnect grapgics device (either for the local
|
|
* or for the remote list.
|
|
\***************************************************************************/
|
|
BOOL
|
|
DrvSetDisconnectedGraphicsDevice(
|
|
BOOL bLocal)
|
|
{
|
|
const WCHAR DisconnectDeviceName[] = L"\\Device\\Disc";
|
|
NTSTATUS status;
|
|
PGRAPHICS_DEVICE PhysDisp = NULL;
|
|
HANDLE hkRegistry = NULL;
|
|
BOOL bReturn = FALSE;
|
|
|
|
TRACE_INIT(("Drv_Trace:DrvSetDisconnectedGraphicsDevice: \n"));
|
|
|
|
if ((bLocal && gpLocalDiscGraphicsDevice != NULL ) ||
|
|
(!bLocal && gpRemoteDiscGraphicsDevice != NULL))
|
|
{
|
|
TRACE_INIT(("DrvSetDisconnectedGraphicsDevice - Device already set"));
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Allocate a buffer .
|
|
//
|
|
|
|
PhysDisp = (PGRAPHICS_DEVICE) PALLOCMEM(sizeof(GRAPHICS_DEVICE),
|
|
GDITAG_GDEVICE);
|
|
if (PhysDisp)
|
|
{
|
|
UNICODE_STRING DeviceName;
|
|
|
|
//
|
|
// Assign NtDeviceName as "\\Device\\Disc".
|
|
// This has to be have an entry in DEVICEMAP\Video
|
|
//
|
|
RtlInitUnicodeString(&DeviceName,
|
|
L"\\REGISTRY\\Machine\\System\\CurrentControlSet\\Services\\TSDDD\\Device0");
|
|
RtlWriteRegistryValue(RTL_REGISTRY_DEVICEMAP,
|
|
L"VIDEO",
|
|
DisconnectDeviceName,
|
|
REG_SZ,
|
|
DeviceName.Buffer,
|
|
DeviceName.Length + sizeof(UNICODE_NULL));
|
|
|
|
RtlCopyMemory(&(PhysDisp->szNtDeviceName[0]), DisconnectDeviceName, sizeof(DisconnectDeviceName));
|
|
|
|
PhysDisp->numMonitorDevice = 0;
|
|
PhysDisp->MonitorDevices = NULL;
|
|
PhysDisp->stateFlags |= DISPLAY_DEVICE_DISCONNECT;
|
|
PhysDisp->ProtocolType = PROTOCOL_DISCONNECT;
|
|
|
|
|
|
// Read device Configuration from registry
|
|
|
|
hkRegistry = DrvGetRegistryHandleFromDeviceMap(
|
|
PhysDisp,
|
|
DispDriverRegGlobal,
|
|
NULL,
|
|
NULL,
|
|
&status,
|
|
PROTOCOL_DISCONNECT);
|
|
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
DrvGetDeviceConfigurationInformation(PhysDisp, hkRegistry, FALSE);
|
|
ZwCloseKey(hkRegistry);
|
|
bReturn = TRUE;
|
|
}
|
|
|
|
//
|
|
// If we haven't specified a device description set it to something.
|
|
//
|
|
|
|
if ((NT_SUCCESS(status)) &&
|
|
(PhysDisp->DeviceDescription == NULL))
|
|
{
|
|
if (PhysDisp->DeviceDescription = (LPWSTR) PALLOCNOZ(32, GDITAG_DRVSUP))
|
|
{
|
|
hkRegistry = DrvGetRegistryHandleFromDeviceMap(PhysDisp,
|
|
DispDriverRegGlobal,
|
|
NULL,
|
|
PhysDisp->DeviceDescription,
|
|
&status,
|
|
PROTOCOL_DISCONNECT);
|
|
if (hkRegistry)
|
|
{
|
|
ZwCloseKey(hkRegistry);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
if (PhysDisp->DisplayDriverNames != NULL) {
|
|
VFREEMEM(PhysDisp->DisplayDriverNames);
|
|
}
|
|
VFREEMEM(PhysDisp);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Set WinDeviceName (name for extenal reference; usually \\.\DISPLAY#)
|
|
//
|
|
|
|
swprintf(&(PhysDisp->szWinDeviceName[0]),L"WinDisc");
|
|
|
|
PhysDisp->pDeviceHandle = (HANDLE) NULL;
|
|
|
|
//
|
|
// Link the new device at the end so we can enumerate
|
|
// in the right order.
|
|
//
|
|
|
|
if (gpGraphicsDeviceList == NULL)
|
|
{
|
|
gpGraphicsDeviceList = PhysDisp;
|
|
gpGraphicsDeviceListLast = PhysDisp;
|
|
}
|
|
else
|
|
{
|
|
gpGraphicsDeviceListLast->pNextGraphicsDevice = PhysDisp;
|
|
gpGraphicsDeviceListLast = PhysDisp;
|
|
}
|
|
|
|
if (bLocal)
|
|
{
|
|
gpLocalDiscGraphicsDevice = PhysDisp;
|
|
}
|
|
else
|
|
{
|
|
gpRemoteDiscGraphicsDevice = PhysDisp;
|
|
}
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrvEnumDisplaySettings
|
|
*
|
|
* Routines that enumerate the list of modes available in the driver.
|
|
*
|
|
* andreva Created
|
|
\***************************************************************************/
|
|
|
|
NTSTATUS
|
|
DrvEnumDisplaySettings(
|
|
PUNICODE_STRING pstrDeviceName,
|
|
HDEV hdevPrimary,
|
|
DWORD iModeNum,
|
|
LPDEVMODEW lpDevMode,
|
|
DWORD dwFlags)
|
|
{
|
|
GDIFunctionID(DrvEnumDisplaySettings);
|
|
|
|
NTSTATUS retval = STATUS_INVALID_PARAMETER_1;
|
|
PGRAPHICS_DEVICE PhysDisp = NULL;
|
|
USHORT DriverExtraSize;
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvEnumDisplaySettings\n"));
|
|
|
|
//
|
|
// Probe the DeviceName and the DEVMODE.
|
|
//
|
|
|
|
__try
|
|
{
|
|
ProbeForRead(lpDevMode, sizeof(DEVMODEW), sizeof(USHORT));
|
|
|
|
DriverExtraSize = lpDevMode->dmDriverExtra;
|
|
|
|
ProbeForWrite(lpDevMode,
|
|
sizeof(DEVMODEW) + DriverExtraSize,
|
|
sizeof(USHORT));
|
|
|
|
|
|
if (lpDevMode->dmSize != sizeof(DEVMODEW))
|
|
{
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
if (pstrDeviceName)
|
|
{
|
|
PhysDisp = DrvGetDeviceFromName(pstrDeviceName, UserMode);
|
|
}
|
|
else
|
|
{
|
|
PDEVOBJ pdo(hdevPrimary);
|
|
|
|
if (pdo.bValid())
|
|
{
|
|
PhysDisp = pdo.ppdev->pGraphicsDevice;
|
|
}
|
|
}
|
|
|
|
if (PhysDisp)
|
|
{
|
|
//
|
|
// -3 means we want the Monitor prefered DEVMODE
|
|
//
|
|
if (iModeNum == (DWORD) -3) // ENUM_MONITOR_PREFERED
|
|
{
|
|
retval = DrvGetPreferredMode(lpDevMode, PhysDisp);
|
|
}
|
|
|
|
//
|
|
// -2 means we want the registry DEVMODE to do matching on the
|
|
// client side.
|
|
//
|
|
|
|
else if (iModeNum == (DWORD) -2) // ENUM_REGISTRY_SETTINGS
|
|
{
|
|
PDEVMODEW pdevmode;
|
|
|
|
TRACE_INIT(("DrvEnumDisp: -2 mode\n"));
|
|
|
|
pdevmode = (PDEVMODEW) PALLOCMEM(sizeof(DEVMODEW) + MAXUSHORT,
|
|
GDITAG_DEVMODE);
|
|
|
|
if (pdevmode)
|
|
{
|
|
pdevmode->dmSize = 0xDDDD;
|
|
pdevmode->dmDriverExtra = MAXUSHORT;
|
|
|
|
retval = DrvGetDisplayDriverParameters(PhysDisp,
|
|
pdevmode,
|
|
FALSE,
|
|
FALSE);
|
|
|
|
if (NT_SUCCESS(retval))
|
|
{
|
|
__try
|
|
{
|
|
DriverExtraSize = min(DriverExtraSize,
|
|
pdevmode->dmDriverExtra);
|
|
|
|
RtlCopyMemory(lpDevMode + 1,
|
|
pdevmode + 1,
|
|
DriverExtraSize);
|
|
|
|
RtlCopyMemory(lpDevMode,
|
|
pdevmode,
|
|
sizeof(DEVMODEW));
|
|
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
retval = STATUS_INVALID_PARAMETER_3;
|
|
}
|
|
}
|
|
VFREEMEM(pdevmode);
|
|
}
|
|
|
|
}
|
|
//
|
|
// -1 means returns the current device mode.
|
|
// We store the full DEVMODE in the
|
|
//
|
|
|
|
else if (iModeNum == (DWORD) -1) // ENUM_CURRENT_SETTINGS
|
|
{
|
|
PPDEV ppdev;
|
|
|
|
TRACE_INIT(("DrvEnumDisp: -1 mode\n"));
|
|
|
|
//
|
|
// Since we are accessing variable fields off of this device,
|
|
// acquire the lock for them.
|
|
//
|
|
|
|
GreAcquireSemaphoreEx(ghsemDriverMgmt, SEMORDER_DRIVERMGMT, NULL);
|
|
|
|
//
|
|
// Find the currently adtive PDEV on this device.
|
|
//
|
|
|
|
for (ppdev = gppdevList; ppdev != NULL; ppdev = ppdev->ppdevNext)
|
|
{
|
|
PDEVOBJ po((HDEV) ppdev);
|
|
|
|
//
|
|
// Also need to check VGA(alias) device
|
|
//
|
|
if (((po.ppdev->pGraphicsDevice == PhysDisp) ||
|
|
((po.ppdev->pGraphicsDevice == PhysDisp->pVgaDevice) &&
|
|
(po.ppdev->pGraphicsDevice != NULL)))
|
|
&& (!po.bDeleted()))
|
|
{
|
|
__try
|
|
{
|
|
DriverExtraSize = min(DriverExtraSize,
|
|
po.ppdev->ppdevDevmode->dmDriverExtra);
|
|
|
|
//
|
|
// We know the DEVMODE we called the driver with is of
|
|
// size sizeof(DEVMODEW)
|
|
//
|
|
|
|
RtlCopyMemory(lpDevMode + 1,
|
|
po.ppdev->ppdevDevmode + 1,
|
|
DriverExtraSize);
|
|
|
|
RtlCopyMemory(lpDevMode,
|
|
po.ppdev->ppdevDevmode,
|
|
sizeof(DEVMODEW));
|
|
|
|
retval = STATUS_SUCCESS;
|
|
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
retval = STATUS_INVALID_PARAMETER_3;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
GreReleaseSemaphoreEx(ghsemDriverMgmt);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We don't need synchronize access to the list of modes.
|
|
// Who cares.
|
|
//
|
|
|
|
DrvBuildDevmodeList(PhysDisp, FALSE);
|
|
|
|
//
|
|
// now return the information
|
|
//
|
|
|
|
if ( (PhysDisp->cbdevmodeInfo == 0) ||
|
|
(PhysDisp->devmodeInfo == NULL) )
|
|
{
|
|
WARNING("EnumDisplaySettings PhysDisp is inconsistent\n");
|
|
retval = STATUS_UNSUCCESSFUL;
|
|
}
|
|
else
|
|
{
|
|
LPDEVMODEW lpdm = NULL;
|
|
DWORD i, count;
|
|
|
|
retval = STATUS_INVALID_PARAMETER_2;
|
|
|
|
if (iModeNum < PhysDisp->numRawModes)
|
|
{
|
|
if (dwFlags & EDS_RAWMODE)
|
|
lpdm = PhysDisp->devmodeMarks[iModeNum].pDevMode;
|
|
else
|
|
{
|
|
for (i = 0, count = 0; i < PhysDisp->numRawModes; i++)
|
|
{
|
|
if (PhysDisp->devmodeMarks[i].bPruned)
|
|
continue;
|
|
if (count == iModeNum)
|
|
{
|
|
lpdm = PhysDisp->devmodeMarks[i].pDevMode;
|
|
break;
|
|
}
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (lpdm)
|
|
{
|
|
__try
|
|
{
|
|
DriverExtraSize = min(DriverExtraSize,
|
|
lpdm->dmDriverExtra);
|
|
|
|
RtlZeroMemory(lpDevMode, sizeof(*lpDevMode));
|
|
|
|
//
|
|
// Check the size since the devmode returned
|
|
// by the driver can be smaller than the current
|
|
// size.
|
|
//
|
|
|
|
RtlCopyMemory(lpDevMode + 1,
|
|
((PUCHAR)lpdm) + lpdm->dmSize,
|
|
DriverExtraSize);
|
|
|
|
RtlCopyMemory(lpDevMode,
|
|
lpdm,
|
|
min(sizeof(DEVMODEW), lpdm->dmSize));
|
|
|
|
retval = STATUS_SUCCESS;
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
retval = STATUS_INVALID_PARAMETER_3;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// As an acceleration, we will only free the list if the call
|
|
// failed because "i" was too large, so that listing all the modes
|
|
// does not require building the list each time.
|
|
//
|
|
|
|
if (retval == STATUS_INVALID_PARAMETER_2)
|
|
{
|
|
//
|
|
// Free up the resources - as long as it's not the VGA.
|
|
// Assume the VGA is always first
|
|
//
|
|
|
|
if (PhysDisp != &gFullscreenGraphicsDevice)
|
|
{
|
|
PhysDisp->cbdevmodeInfo = 0;
|
|
|
|
if (PhysDisp->devmodeInfo)
|
|
{
|
|
VFREEMEM(PhysDisp->devmodeInfo);
|
|
PhysDisp->devmodeInfo = NULL;
|
|
}
|
|
if (PhysDisp->devmodeMarks)
|
|
{
|
|
VFREEMEM(PhysDisp->devmodeMarks);
|
|
PhysDisp->devmodeMarks = NULL;
|
|
}
|
|
PhysDisp->numRawModes = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the driver extra size
|
|
//
|
|
|
|
if (retval == STATUS_SUCCESS)
|
|
{
|
|
__try
|
|
{
|
|
lpDevMode->dmDriverExtra = DriverExtraSize;
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
retval = GetExceptionCode();
|
|
}
|
|
}
|
|
|
|
return (retval);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* DrvEnumDisplayDevices
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
|
|
NTSTATUS
|
|
DrvEnumDisplayDevices(
|
|
PUNICODE_STRING pstrDeviceName,
|
|
HDEV hdevPrimary,
|
|
DWORD iDevNum,
|
|
LPDISPLAY_DEVICEW lpDisplayDevice,
|
|
DWORD dwFlags,
|
|
MODE PreviousMode)
|
|
{
|
|
PGRAPHICS_DEVICE PhysDisp;
|
|
ULONG cbSize;
|
|
ULONG cCount = 0;
|
|
PDEVICE_OBJECT pdo = NULL;
|
|
NTSTATUS retStatus = STATUS_SUCCESS;
|
|
|
|
UNREFERENCED_PARAMETER(dwFlags);
|
|
UNREFERENCED_PARAMETER(hdevPrimary);
|
|
|
|
if (pstrDeviceName == NULL)
|
|
{
|
|
//
|
|
// Start enumerating at 0 ...
|
|
//
|
|
|
|
for (PhysDisp = gpGraphicsDeviceList;
|
|
PhysDisp != NULL;
|
|
PhysDisp = PhysDisp->pNextGraphicsDevice, cCount++)
|
|
{
|
|
//
|
|
// Do not enumerate the disconnected DD for user mode callers.
|
|
// Also if we are not on the physical console, do not enumerate
|
|
// drivers other than current protocol driver.
|
|
//
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
if((PhysDisp->stateFlags & DISPLAY_DEVICE_DISCONNECT) ||
|
|
(gProtocolType != PROTOCOL_CONSOLE && PhysDisp->ProtocolType != gProtocolType))
|
|
{
|
|
cCount--;
|
|
continue;
|
|
}
|
|
|
|
}
|
|
|
|
if (cCount == iDevNum)
|
|
break;
|
|
}
|
|
|
|
if (PhysDisp == NULL)
|
|
{
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
PDEVICE_RELATIONS pDeviceRelations;
|
|
|
|
//pDeviceHandle migth be NULL in the case of the disconnected DD which
|
|
//has no associated miniport.
|
|
|
|
if (PhysDisp->pPhysDeviceHandle) {
|
|
pdo = (PDEVICE_OBJECT)PhysDisp->pPhysDeviceHandle;
|
|
}
|
|
else if ((PDEVICE_OBJECT)PhysDisp->pDeviceHandle != NULL) {
|
|
if (NT_SUCCESS(DrvSendPnPIrp((PDEVICE_OBJECT)PhysDisp->pDeviceHandle,
|
|
TargetDeviceRelation,
|
|
&pDeviceRelations) )
|
|
)
|
|
{
|
|
pdo = pDeviceRelations->Objects[0];
|
|
ASSERTGDI(pDeviceRelations->Count == 1,
|
|
"TargetDeviceRelation should only get one PDO\n");
|
|
|
|
ExFreePool(pDeviceRelations);
|
|
}
|
|
}else{
|
|
TRACE_INIT(("DrvEnumDisplayDevices - processing the disconnected GRAPHICS_DEVICE\n"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UpdateMonitorDevices();
|
|
|
|
PhysDisp = DrvGetDeviceFromName(pstrDeviceName, PreviousMode);
|
|
if (PhysDisp == NULL)
|
|
return STATUS_UNSUCCESSFUL;
|
|
if (iDevNum >= PhysDisp->numMonitorDevice)
|
|
return STATUS_UNSUCCESSFUL;
|
|
pdo = (PDEVICE_OBJECT)PhysDisp->MonitorDevices[iDevNum].pdo;
|
|
}
|
|
|
|
//
|
|
// We found the device, so the call will be successful unless
|
|
// we except later on.
|
|
//
|
|
|
|
__try
|
|
{
|
|
PVOID pBuffer;
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// Capture the input buffer length
|
|
//
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvEnumDisplayDevices %d\n", iDevNum));
|
|
|
|
if (PreviousMode == UserMode)
|
|
{
|
|
cbSize = ProbeAndReadUlong(&(lpDisplayDevice->cb));
|
|
ProbeForWrite(lpDisplayDevice, cbSize, sizeof(DWORD));
|
|
}
|
|
else
|
|
{
|
|
ASSERTGDI(lpDisplayDevice >= (LPDISPLAY_DEVICEW const)MM_USER_PROBE_ADDRESS,
|
|
"Bad kernel mode address\n");
|
|
|
|
cbSize = lpDisplayDevice->cb;
|
|
}
|
|
|
|
RtlZeroMemory(lpDisplayDevice, cbSize);
|
|
|
|
if (cbSize >= FIELD_OFFSET(DISPLAY_DEVICEW, DeviceName))
|
|
{
|
|
lpDisplayDevice->cb = FIELD_OFFSET(DISPLAY_DEVICEW, DeviceName);
|
|
}
|
|
if (cbSize >= FIELD_OFFSET(DISPLAY_DEVICEW, DeviceString))
|
|
{
|
|
lpDisplayDevice->cb = FIELD_OFFSET(DISPLAY_DEVICEW, DeviceString);
|
|
|
|
if (pstrDeviceName == NULL)
|
|
RtlCopyMemory(lpDisplayDevice->DeviceName,
|
|
PhysDisp->szWinDeviceName,
|
|
sizeof(PhysDisp->szWinDeviceName));
|
|
else
|
|
swprintf(lpDisplayDevice->DeviceName,
|
|
L"%ws\\Monitor%d",
|
|
PhysDisp->szWinDeviceName, iDevNum);
|
|
lpDisplayDevice->DeviceName[31] = 0;
|
|
}
|
|
if (cbSize >= FIELD_OFFSET(DISPLAY_DEVICEW, StateFlags))
|
|
{
|
|
|
|
lpDisplayDevice->cb = FIELD_OFFSET(DISPLAY_DEVICEW, StateFlags);
|
|
lpDisplayDevice->DeviceString[0] = 0;
|
|
|
|
if (pstrDeviceName == NULL)
|
|
{
|
|
if (PhysDisp->DeviceDescription)
|
|
wcsncpy(lpDisplayDevice->DeviceString, PhysDisp->DeviceDescription, 127);
|
|
}
|
|
else if (pdo)
|
|
{
|
|
//
|
|
// Get the name for this device.
|
|
// Documentation says to try in a loop.
|
|
//
|
|
cCount = 256;
|
|
while (1)
|
|
{
|
|
pBuffer = PALLOCNOZ(cCount, 'ddeG');
|
|
if (pBuffer == NULL)
|
|
{
|
|
retStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
status = IoGetDeviceProperty(pdo,
|
|
DevicePropertyDeviceDescription,
|
|
cCount,
|
|
pBuffer,
|
|
&cCount);
|
|
if (status == STATUS_BUFFER_TOO_SMALL)
|
|
{
|
|
VFREEMEM(pBuffer);
|
|
continue;
|
|
}
|
|
else if (status == STATUS_SUCCESS)
|
|
{
|
|
wcsncpy(lpDisplayDevice->DeviceString, (LPWSTR)pBuffer, 127);
|
|
VFREEMEM(pBuffer);
|
|
break;
|
|
}
|
|
VFREEMEM(pBuffer);
|
|
break;
|
|
}
|
|
}
|
|
lpDisplayDevice->DeviceString[127] = 0;
|
|
}
|
|
if (cbSize >= FIELD_OFFSET(DISPLAY_DEVICEW, DeviceID))
|
|
{
|
|
lpDisplayDevice->cb = FIELD_OFFSET(DISPLAY_DEVICEW, DeviceID);
|
|
|
|
if (pstrDeviceName == NULL)
|
|
lpDisplayDevice->StateFlags = PhysDisp->stateFlags & 0x0FFFFFFF;
|
|
else
|
|
lpDisplayDevice->StateFlags = PhysDisp->MonitorDevices[iDevNum].flag & 0x0FFFFFFF;
|
|
}
|
|
|
|
if (cbSize >= FIELD_OFFSET(DISPLAY_DEVICEW, DeviceKey))
|
|
{
|
|
lpDisplayDevice->cb = FIELD_OFFSET(DISPLAY_DEVICEW, DeviceKey);
|
|
|
|
lpDisplayDevice->DeviceID[0] = 0;
|
|
|
|
if (pdo)
|
|
{
|
|
cCount = 256;
|
|
while (1)
|
|
{
|
|
pBuffer = PALLOCNOZ(cCount, 'ddeG');
|
|
if (pBuffer == NULL)
|
|
{
|
|
retStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
status = IoGetDeviceProperty(pdo,
|
|
DevicePropertyHardwareID,
|
|
cCount,
|
|
pBuffer,
|
|
&cCount);
|
|
if (status == STATUS_BUFFER_TOO_SMALL)
|
|
{
|
|
VFREEMEM(pBuffer);
|
|
continue;
|
|
}
|
|
else if (status == STATUS_SUCCESS)
|
|
{
|
|
wcsncpy(lpDisplayDevice->DeviceID, (LPWSTR)pBuffer, 127);
|
|
VFREEMEM(pBuffer);
|
|
break;
|
|
}
|
|
VFREEMEM(pBuffer);
|
|
break;
|
|
}
|
|
// For Monitor devices, we will make the ID unique.
|
|
// Applet and GDIs are expecting this behavior.
|
|
if (pstrDeviceName != NULL)
|
|
{
|
|
lpDisplayDevice->DeviceID[127] = 0;
|
|
cCount = wcslen(lpDisplayDevice->DeviceID)+1;
|
|
if (cCount < 126)
|
|
{
|
|
lpDisplayDevice->DeviceID[cCount-1] = L'\\';
|
|
status = IoGetDeviceProperty(pdo,
|
|
DevicePropertyDriverKeyName,
|
|
(127-cCount)*sizeof(WCHAR),
|
|
(PBYTE)(lpDisplayDevice->DeviceID+cCount),
|
|
&cCount);
|
|
}
|
|
}
|
|
}
|
|
lpDisplayDevice->DeviceID[127] = 0;
|
|
}
|
|
if (cbSize >= sizeof(DISPLAY_DEVICEW))
|
|
{
|
|
lpDisplayDevice->cb = sizeof(DISPLAY_DEVICEW);
|
|
|
|
lpDisplayDevice->DeviceKey[0] = 0;
|
|
|
|
if (pstrDeviceName == NULL)
|
|
{
|
|
DrvGetRegistryHandleFromDeviceMap(PhysDisp, DispDriverRegKey, NULL, lpDisplayDevice->DeviceKey, NULL, gProtocolType);
|
|
}
|
|
else
|
|
{
|
|
NTSTATUS Status;
|
|
WCHAR driverRegistryPath[127];
|
|
|
|
Status = IoGetDeviceProperty(pdo,
|
|
DevicePropertyDriverKeyName,
|
|
127*sizeof(WCHAR),
|
|
(PBYTE)driverRegistryPath,
|
|
&cCount);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
wcscpy(lpDisplayDevice->DeviceKey, REGSTR_CCS);
|
|
cCount = wcslen(lpDisplayDevice->DeviceKey);
|
|
wcsncpy(lpDisplayDevice->DeviceKey+cCount, L"\\Control\\Class\\", 127-cCount);
|
|
cCount = wcslen(lpDisplayDevice->DeviceKey);
|
|
wcsncpy(lpDisplayDevice->DeviceKey+cCount, driverRegistryPath, 127-cCount);
|
|
}
|
|
}
|
|
lpDisplayDevice->DeviceKey[127] = 0;
|
|
}
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
WARNINGX(100);
|
|
retStatus = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
if (pstrDeviceName == NULL && pdo && (PhysDisp->pPhysDeviceHandle == NULL))
|
|
{
|
|
ObDereferenceObject(pdo);
|
|
}
|
|
|
|
return retStatus;
|
|
}
|
|
|
|
|
|
/******************************Public*Routine******************************\
|
|
* DrvGetDriverAccelerationsLevel()
|
|
*
|
|
* Reads the driver acceleration 'filter' level from the registry. This
|
|
* values determines how we let the display driver do in terms of
|
|
* accelerations, where a value of 0 means full acceleration.
|
|
*
|
|
* 20-Aug-1998 -by- Hideyuki Nagase [hideyukn]
|
|
* Lifted it from AndrewGo's code.
|
|
\**************************************************************************/
|
|
|
|
#define ACCEL_REGKEY L"Acceleration.Level"
|
|
#define CAPABLE_REGKEY L"CapabilityOverride" // Driver capable override
|
|
#define ACCEL_BUFSIZE (sizeof(KEY_VALUE_FULL_INFORMATION) + sizeof(ACCEL_REGKEY) + sizeof(DWORD))
|
|
#define CAPABLE_BUFSIZE (sizeof(KEY_VALUE_FULL_INFORMATION) + sizeof(CAPABLE_REGKEY) + sizeof(DWORD))
|
|
|
|
DWORD
|
|
DrvGetDriverAccelerationsLevel(
|
|
PGRAPHICS_DEVICE pGraphicsDevice
|
|
)
|
|
{
|
|
HANDLE hkRegistry;
|
|
UNICODE_STRING UnicodeString;
|
|
DWORD dwReturn = DRIVER_ACCELERATIONS_INVALID;
|
|
BYTE buffer[ACCEL_BUFSIZE];
|
|
ULONG Length = ACCEL_BUFSIZE;
|
|
PKEY_VALUE_FULL_INFORMATION Information = (PKEY_VALUE_FULL_INFORMATION) buffer;
|
|
WCHAR aszValuename[] = ACCEL_REGKEY;
|
|
|
|
if (pGraphicsDevice == (PGRAPHICS_DEVICE) DDML_DRIVER)
|
|
{
|
|
dwReturn = 0;
|
|
}
|
|
|
|
if (dwReturn == DRIVER_ACCELERATIONS_INVALID)
|
|
{
|
|
//
|
|
// See adaptor specific setting first.
|
|
//
|
|
|
|
hkRegistry = DrvGetRegistryHandleFromDeviceMap(
|
|
pGraphicsDevice,
|
|
DispDriverRegGlobal,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
gProtocolType);
|
|
|
|
if (hkRegistry)
|
|
{
|
|
RtlInitUnicodeString(&UnicodeString, aszValuename);
|
|
|
|
if (NT_SUCCESS(ZwQueryValueKey(hkRegistry,
|
|
&UnicodeString,
|
|
KeyValueFullInformation,
|
|
Information,
|
|
Length,
|
|
&Length)))
|
|
{
|
|
dwReturn = *(LPDWORD) ((((PUCHAR)Information) +
|
|
Information->DataOffset));
|
|
}
|
|
|
|
ZwCloseKey(hkRegistry);
|
|
}
|
|
}
|
|
|
|
if (dwReturn == DRIVER_ACCELERATIONS_INVALID)
|
|
{
|
|
//
|
|
// If there is nothing specified, assume full acceleration.
|
|
//
|
|
|
|
dwReturn = DRIVER_ACCELERATIONS_FULL;
|
|
}
|
|
else if (dwReturn > DRIVER_ACCELERATIONS_NONE)
|
|
{
|
|
//
|
|
// If there is something invalid value, assume no acceleration.
|
|
//
|
|
|
|
dwReturn = DRIVER_ACCELERATIONS_NONE;
|
|
}
|
|
|
|
if (G_fDoubleDpi)
|
|
{
|
|
//
|
|
// Always set accelerations to 'none' when operating in double-the-
|
|
// dpi mode, so that panning.cxx can create a virtual display
|
|
// larger than the actual physical display.
|
|
//
|
|
|
|
dwReturn = DRIVER_ACCELERATIONS_NONE;
|
|
}
|
|
|
|
#if DBG
|
|
if (pGraphicsDevice != (PGRAPHICS_DEVICE) DDML_DRIVER)
|
|
{
|
|
DbgPrint("GDI: DriverAccelerationLevel on %ws is %d\n",
|
|
pGraphicsDevice->szWinDeviceName, dwReturn);
|
|
}
|
|
#endif
|
|
|
|
return(dwReturn);
|
|
}
|
|
|
|
DWORD
|
|
DrvGetDriverCapableOverRide(
|
|
PGRAPHICS_DEVICE pGraphicsDevice
|
|
)
|
|
{
|
|
HANDLE hkRegistry;
|
|
UNICODE_STRING UnicodeString;
|
|
DWORD dwReturn = DRIVER_CAPABLE_ALL;
|
|
BYTE buffer[CAPABLE_BUFSIZE];
|
|
ULONG ulLength = CAPABLE_BUFSIZE;
|
|
ULONG ulActualLength;
|
|
PKEY_VALUE_FULL_INFORMATION Information = (PKEY_VALUE_FULL_INFORMATION)buffer;
|
|
WCHAR aszValuename[] = CAPABLE_REGKEY;
|
|
|
|
if ( pGraphicsDevice == (PGRAPHICS_DEVICE)DDML_DRIVER )
|
|
{
|
|
return DRIVER_CAPABLE_ALL;
|
|
}
|
|
|
|
//
|
|
// See adaptor specific setting first.
|
|
//
|
|
hkRegistry = DrvGetRegistryHandleFromDeviceMap(
|
|
pGraphicsDevice,
|
|
DispDriverRegGlobal,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
gProtocolType);
|
|
|
|
if ( hkRegistry )
|
|
{
|
|
RtlInitUnicodeString(&UnicodeString, aszValuename);
|
|
|
|
if ( NT_SUCCESS(ZwQueryValueKey(hkRegistry,
|
|
&UnicodeString,
|
|
KeyValueFullInformation,
|
|
Information,
|
|
ulLength,
|
|
&ulActualLength)) )
|
|
{
|
|
dwReturn = *(LPDWORD)((((PUCHAR)Information)
|
|
+ Information->DataOffset));
|
|
}
|
|
|
|
ZwClose(hkRegistry);
|
|
}
|
|
|
|
#if DBG
|
|
if ( pGraphicsDevice != (PGRAPHICS_DEVICE) DDML_DRIVER )
|
|
{
|
|
DbgPrint("GDI: DriverCapableOverride on %ws is %d\n",
|
|
pGraphicsDevice->szWinDeviceName, dwReturn);
|
|
}
|
|
#endif
|
|
|
|
return(dwReturn);
|
|
}// DrvGetDriverCapableOverRide()
|
|
|
|
VOID
|
|
DrvUpdateAttachFlag(PGRAPHICS_DEVICE PhysDisp, DWORD attach)
|
|
{
|
|
HANDLE hkRegistry = DrvGetRegistryHandleFromDeviceMap(PhysDisp,
|
|
DispDriverRegHardwareProfileCreate,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
gProtocolType);
|
|
if (hkRegistry)
|
|
{
|
|
RtlWriteRegistryValue(RTL_REGISTRY_HANDLE,
|
|
(PCWSTR)hkRegistry,
|
|
(PWSTR)AttachedSettings[1],
|
|
REG_DWORD,
|
|
&attach,
|
|
sizeof(DWORD));
|
|
ZwCloseKey(hkRegistry);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* DrvEnableMDEV
|
|
*
|
|
* Enables a display MDEV.
|
|
*
|
|
* Should only be called from USER under the global CRIT
|
|
*
|
|
\***************************************************************************/
|
|
|
|
ULONG gtmpAssertModeFailed;
|
|
|
|
BOOL
|
|
DrvEnableDisplay(
|
|
HDEV hdev
|
|
)
|
|
{
|
|
PDEVOBJ po(hdev);
|
|
|
|
TRACE_INIT(("\nDrv_Trace: DrvEnableDisplay: Enter\n"));
|
|
|
|
ASSERTGDI(po.bValid(), "HDEV failure\n");
|
|
ASSERTGDI(po.bDisplayPDEV(), "HDEV is not a display device\n");
|
|
ASSERTGDI(po.bDisabled(), "HDEV must be disabled to call enable\n");
|
|
ASSERTGDI(po.ppdev->pGraphicsDevice, "HDEV must be on a device\n");
|
|
|
|
//
|
|
// Acquire the display locks here because we may get called from within
|
|
// ChangeDisplaySettings to enable\disable a particular device when
|
|
// creating the new HDEVs.
|
|
//
|
|
|
|
GreAcquireSemaphoreEx(ghsemShareDevLock, SEMORDER_SHAREDEVLOCK, NULL);
|
|
GreAcquireSemaphoreEx(po.hsemDevLock(), SEMORDER_DEVLOCK, NULL);
|
|
GreAcquireSemaphoreEx(po.hsemPointer(), SEMORDER_POINTER, po.hsemDevLock());
|
|
GreEnterMonitoredSection(po.ppdev, WD_DEVLOCK);
|
|
|
|
//
|
|
// Enable the screen
|
|
// Repeat the call until it works.
|
|
//
|
|
|
|
bSetDeviceSessionUsage(po.ppdev->pGraphicsDevice, TRUE);
|
|
|
|
gtmpAssertModeFailed = 0;
|
|
while (!((*PPFNDRV(po,AssertMode))(po.dhpdev(), TRUE)))
|
|
{
|
|
gtmpAssertModeFailed = 1;
|
|
}
|
|
|
|
//
|
|
// Clear the PDEV for use
|
|
//
|
|
|
|
po.bDisabled(FALSE);
|
|
|
|
GreExitMonitoredSection(po.ppdev, WD_DEVLOCK);
|
|
GreReleaseSemaphoreEx(po.hsemPointer());
|
|
GreReleaseSemaphoreEx(po.hsemDevLock());
|
|
GreReleaseSemaphoreEx(ghsemShareDevLock);
|
|
|
|
//
|
|
// Allow DirectDraw to be reenabled.
|
|
//
|
|
|
|
GreResumeDirectDraw(hdev, FALSE);
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvEnableDisplay: Leave\n"));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
DrvEnableMDEV(
|
|
PMDEV pmdev,
|
|
BOOL bHardware
|
|
)
|
|
{
|
|
GDIFunctionID(DrvEnableMDEV);
|
|
|
|
ULONG i;
|
|
ERESOURCE_THREAD lockOwner;
|
|
|
|
TRACE_INIT(("\nDrv_Trace: DrvEnableMDEV: Enter\n"));
|
|
|
|
PDEVOBJ poParent(pmdev->hdevParent);
|
|
|
|
#if MDEV_STACK_TRACE_LENGTH
|
|
LONG lMDEVTraceEntry, lMDEVTraceEntryNext;
|
|
ULONG StackEntries, UserStackEntry;
|
|
|
|
do
|
|
{
|
|
lMDEVTraceEntry = glMDEVTrace;
|
|
lMDEVTraceEntryNext = lMDEVTraceEntry + 1;
|
|
if (lMDEVTraceEntryNext >= gcMDEVTraceLength) lMDEVTraceEntryNext = 0;
|
|
} while (InterlockedCompareExchange(&glMDEVTrace, lMDEVTraceEntryNext, lMDEVTraceEntry) != lMDEVTraceEntry);
|
|
|
|
RtlZeroMemory(&gMDEVTrace[lMDEVTraceEntry],
|
|
sizeof(gMDEVTrace[lMDEVTraceEntry]));
|
|
gMDEVTrace[lMDEVTraceEntry].pMDEV = pmdev;
|
|
gMDEVTrace[lMDEVTraceEntry].API = ((bHardware) ? DrvEnableMDEV_HWOn : DrvEnableMDEV_FromGRE);
|
|
StackEntries = sizeof(gMDEVTrace[lMDEVTraceEntry].Trace)/sizeof(gMDEVTrace[lMDEVTraceEntry].Trace[0]);
|
|
UserStackEntry = RtlWalkFrameChain((PVOID *)gMDEVTrace[lMDEVTraceEntry].Trace,
|
|
StackEntries,
|
|
0);
|
|
StackEntries -= UserStackEntry;
|
|
RtlWalkFrameChain((PVOID *)&gMDEVTrace[lMDEVTraceEntry].Trace[UserStackEntry],
|
|
StackEntries,
|
|
1);
|
|
#endif
|
|
|
|
for (i = 0; i < pmdev->chdev; i++)
|
|
{
|
|
PDEVOBJ po(pmdev->Dev[i].hdev);
|
|
if (bHardware ||
|
|
((po.ppdev->pGraphicsDevice->stateFlags & DISPLAY_DEVICE_DUALVIEW) &&
|
|
gbInvalidateDualView &&
|
|
po.bDisabled())
|
|
)
|
|
{
|
|
DrvEnableDisplay(po.hdev());
|
|
}
|
|
}
|
|
|
|
//
|
|
// Wait for the display to become available and unlock it.
|
|
//
|
|
|
|
GreAcquireSemaphoreEx(ghsemShareDevLock, SEMORDER_SHAREDEVLOCK, NULL);
|
|
GreAcquireSemaphoreEx(poParent.hsemDevLock(), SEMORDER_DEVLOCK, NULL);
|
|
GreAcquireSemaphoreEx(poParent.hsemPointer(), SEMORDER_POINTER, poParent.hsemDevLock());
|
|
GreEnterMonitoredSection(poParent.ppdev, WD_DEVLOCK);
|
|
|
|
if (bHardware)
|
|
{
|
|
poParent.bDisabled(FALSE);
|
|
}
|
|
|
|
//
|
|
// Get the palette
|
|
//
|
|
|
|
XEPALOBJ pal(poParent.ppalSurf());
|
|
ASSERTGDI(pal.bValid(), "EPALOBJ failure\n");
|
|
|
|
if (pal.bIsPalManaged())
|
|
{
|
|
ASSERTGDI(PPFNVALID(poParent,SetPalette), "ERROR palette is not managed");
|
|
|
|
(*PPFNDRV(poParent,SetPalette))(poParent.dhpdev(),
|
|
(PALOBJ *) &pal,
|
|
0,
|
|
0,
|
|
pal.cEntries());
|
|
}
|
|
else if (pmdev->chdev > 1)
|
|
{
|
|
//
|
|
// Only for multi-monitor case.
|
|
//
|
|
|
|
PDEVOBJ pdoChild;
|
|
|
|
for (i = 0; i < pmdev->chdev; i++)
|
|
{
|
|
pdoChild.vInit(pmdev->Dev[i].hdev);
|
|
|
|
if (pdoChild.bIsPalManaged())
|
|
{
|
|
XEPALOBJ palChild(pdoChild.ppalSurf());
|
|
|
|
// Don't use PPFNDRV(pdoChild,...) since need to send
|
|
// this through DDML.
|
|
|
|
pdoChild.pfnSetPalette()(pdoChild.dhpdevParent(),
|
|
(PALOBJ *) &palChild,
|
|
0,
|
|
0,
|
|
palChild.cEntries());
|
|
|
|
break;
|
|
}
|
|
|
|
pdoChild.vInit(NULL);
|
|
}
|
|
|
|
// Even if parent is not pal-managed, but any of children are palette
|
|
// managed, need to reset one of them (only 1 is enough, since
|
|
// colour table is shared across all device palette)
|
|
//
|
|
// Realize halftone palette to secondary device
|
|
|
|
if (pdoChild.bValid())
|
|
{
|
|
KdPrint(("GDI DDML: Primary is not 8bpp, but one of display is 8bpp\n"));
|
|
|
|
if (!DrvRealizeHalftonePalette(pdoChild.hdev(),TRUE))
|
|
{
|
|
KdPrint(("GDI DDML: Failed to realize HT palette on secondary\n"));
|
|
}
|
|
}
|
|
}
|
|
|
|
GreExitMonitoredSection(poParent.ppdev, WD_DEVLOCK);
|
|
GreReleaseSemaphoreEx(poParent.hsemPointer());
|
|
GreReleaseSemaphoreEx(poParent.hsemDevLock());
|
|
GreReleaseSemaphoreEx(ghsemShareDevLock);
|
|
|
|
if (bHardware)
|
|
{
|
|
//
|
|
// Allow DirectDraw to be reenabled.
|
|
//
|
|
|
|
GreResumeDirectDraw(pmdev->hdevParent, FALSE);
|
|
}
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvEnableMDEV: Leave\n"));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* DrvDisableDisplay
|
|
*
|
|
* Disables a display device.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
BOOL
|
|
DrvDisableDisplay(
|
|
HDEV hdev,
|
|
BOOL bClear
|
|
)
|
|
{
|
|
GDIFunctionID(DrvDisableDisplay);
|
|
|
|
BOOL bRet;
|
|
PDEVOBJ po(hdev);
|
|
|
|
TRACE_INIT(("\nDrv_Trace: DrvDisableDisplay: Enter\n"));
|
|
|
|
ASSERTGDI(po.bValid(), "HDEV failure\n");
|
|
ASSERTGDI(po.bDisplayPDEV(), "HDEV is not a display device\n");
|
|
ASSERTGDI(!po.bDisabled(), "HDEV must be enabled to call disable\n");
|
|
|
|
//
|
|
// Disable DirectDraw.
|
|
//
|
|
|
|
GreSuspendDirectDraw(hdev, FALSE);
|
|
|
|
//
|
|
// Acquire the display locks here because we may get called from within
|
|
// ChangeDisplaySettings to enable\disable a particular device when
|
|
// creating the new HDEVs.
|
|
//
|
|
|
|
GreAcquireSemaphoreEx(ghsemShareDevLock, SEMORDER_SHAREDEVLOCK, NULL);
|
|
GreAcquireSemaphoreEx(po.hsemDevLock(), SEMORDER_DEVLOCK, NULL);
|
|
GreAcquireSemaphoreEx(po.hsemPointer(), SEMORDER_POINTER, po.hsemDevLock());
|
|
GreEnterMonitoredSection(po.ppdev, WD_DEVLOCK);
|
|
|
|
if (bClear && !po.bDisabled()) {
|
|
|
|
PDEV *pdev = (PDEV*)hdev;
|
|
ERECTL erclDst(0,0,pdev->pSurface->sizl().cx, pdev->pSurface->sizl().cy);
|
|
|
|
//
|
|
// The display is about to be disabled, but some
|
|
// display drivers don't blank the display.
|
|
// We explicitly blank the display here.
|
|
//
|
|
|
|
(*(pdev->pSurface->pfnBitBlt()))(pdev->pSurface->pSurfobj(),
|
|
(SURFOBJ *) NULL,
|
|
(SURFOBJ *) NULL,
|
|
NULL,
|
|
NULL,
|
|
&erclDst,
|
|
(POINTL *) NULL,
|
|
(POINTL *) NULL,
|
|
NULL,
|
|
NULL,
|
|
0
|
|
);
|
|
}
|
|
|
|
//
|
|
// The device may have something going on, synchronize with it first
|
|
//
|
|
|
|
po.vSync(po.pSurface()->pSurfobj(), NULL, 0);
|
|
|
|
//
|
|
// Disable the screen
|
|
//
|
|
|
|
bRet = (*PPFNDRV(po,AssertMode))(po.dhpdev(), FALSE);
|
|
|
|
if (bRet)
|
|
{
|
|
//
|
|
// Mark the PDEV as disabled
|
|
//
|
|
|
|
bSetDeviceSessionUsage(po.ppdev->pGraphicsDevice, FALSE);
|
|
|
|
po.bDisabled(TRUE);
|
|
|
|
gtmpAssertModeFailed = 0;
|
|
}
|
|
else
|
|
{
|
|
gtmpAssertModeFailed = 1;
|
|
}
|
|
|
|
GreExitMonitoredSection(po.ppdev, WD_DEVLOCK);
|
|
GreReleaseSemaphoreEx(po.hsemPointer());
|
|
GreReleaseSemaphoreEx(po.hsemDevLock());
|
|
GreReleaseSemaphoreEx(ghsemShareDevLock);
|
|
|
|
if (!bRet)
|
|
{
|
|
//
|
|
// We have to undo our 'GreSuspendDirectDraw' call:
|
|
//
|
|
|
|
GreResumeDirectDraw(hdev, FALSE);
|
|
}
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvDisableDisplay: Leave\n"));
|
|
|
|
return (bRet);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* DrvDisableMDEV
|
|
*
|
|
* Disables a display MDEV.
|
|
*
|
|
* Should only be called from USER under the global CRIT
|
|
*
|
|
\***************************************************************************/
|
|
|
|
BOOL
|
|
DrvDisableMDEV(
|
|
PMDEV pmdev,
|
|
BOOL bHardware
|
|
)
|
|
{
|
|
GDIFunctionID(DrvDisableMDEV);
|
|
|
|
BOOL bSuccess = TRUE;
|
|
|
|
#if MDEV_STACK_TRACE_LENGTH
|
|
LONG lMDEVTraceEntry, lMDEVTraceEntryNext;
|
|
ULONG StackEntries, UserStackEntry;
|
|
|
|
do
|
|
{
|
|
lMDEVTraceEntry = glMDEVTrace;
|
|
lMDEVTraceEntryNext = lMDEVTraceEntry + 1;
|
|
if (lMDEVTraceEntryNext >= gcMDEVTraceLength) lMDEVTraceEntryNext = 0;
|
|
} while (InterlockedCompareExchange(&glMDEVTrace, lMDEVTraceEntryNext, lMDEVTraceEntry) != lMDEVTraceEntry);
|
|
|
|
RtlZeroMemory(&gMDEVTrace[lMDEVTraceEntry],
|
|
sizeof(gMDEVTrace[lMDEVTraceEntry]));
|
|
gMDEVTrace[lMDEVTraceEntry].pMDEV = pmdev;
|
|
gMDEVTrace[lMDEVTraceEntry].API = ((bHardware) ? DrvDisableMDEV_HWOff : DrvDisableMDEV_FromGRE);
|
|
StackEntries = sizeof(gMDEVTrace[lMDEVTraceEntry].Trace)/sizeof(gMDEVTrace[lMDEVTraceEntry].Trace[0]);
|
|
UserStackEntry = RtlWalkFrameChain((PVOID *)gMDEVTrace[lMDEVTraceEntry].Trace,
|
|
StackEntries,
|
|
0);
|
|
StackEntries -= UserStackEntry;
|
|
RtlWalkFrameChain((PVOID *)&gMDEVTrace[lMDEVTraceEntry].Trace[UserStackEntry],
|
|
StackEntries,
|
|
1);
|
|
#endif
|
|
|
|
#if TEXTURE_DEMO
|
|
if (ghdevTextureParent)
|
|
{
|
|
WARNING("DrvDisableDisplay: Refused by texture demo");
|
|
return(FALSE);
|
|
}
|
|
#endif
|
|
|
|
TRACE_INIT(("\nDrv_Trace: DrvDisableMDEV: Enter\n"));
|
|
|
|
PDEVOBJ poParent(pmdev->hdevParent);
|
|
|
|
if (bHardware)
|
|
{
|
|
//
|
|
// Disable DirectDraw.
|
|
//
|
|
|
|
GreSuspendDirectDraw(pmdev->hdevParent, FALSE);
|
|
}
|
|
|
|
//
|
|
// Wait for the display to become available and unlock it.
|
|
//
|
|
|
|
GreAcquireSemaphoreEx(ghsemShareDevLock, SEMORDER_SHAREDEVLOCK, NULL);
|
|
GreAcquireSemaphoreEx(poParent.hsemDevLock(), SEMORDER_DEVLOCK, NULL);
|
|
GreAcquireSemaphoreEx(poParent.hsemPointer(), SEMORDER_POINTER, poParent.hsemDevLock());
|
|
GreEnterMonitoredSection(poParent.ppdev, WD_DEVLOCK);
|
|
|
|
ULONG i, j;
|
|
|
|
for (i = 0; i < pmdev->chdev; i++)
|
|
{
|
|
//
|
|
// If Dualview, disable the view anyway
|
|
//
|
|
if (bHardware ||
|
|
((((PDEV *) pmdev->Dev[i].hdev)->pGraphicsDevice->stateFlags & DISPLAY_DEVICE_DUALVIEW) &&
|
|
gbInvalidateDualView)
|
|
)
|
|
{
|
|
if ((bSuccess = DrvDisableDisplay(pmdev->Dev[i].hdev, !bHardware)) == FALSE)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we have a failure while disabling the device, try to reenable the
|
|
// devices that were already disabled.
|
|
//
|
|
|
|
if (bSuccess == FALSE)
|
|
{
|
|
for (j = 0; j < i; j++)
|
|
{
|
|
if (bHardware ||
|
|
((((PDEV *)pmdev->Dev[i].hdev)->pGraphicsDevice->stateFlags & DISPLAY_DEVICE_DUALVIEW) &&
|
|
gbInvalidateDualView)
|
|
)
|
|
{
|
|
while (DrvEnableDisplay(pmdev->Dev[j].hdev) == FALSE)
|
|
{
|
|
ASSERTGDI(FALSE, "We are hosed in Disable MDEV - try again!\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bSuccess)
|
|
{
|
|
if (bHardware)
|
|
{
|
|
poParent.bDisabled(TRUE);
|
|
}
|
|
}
|
|
|
|
GreExitMonitoredSection(poParent.ppdev, WD_DEVLOCK);
|
|
GreReleaseSemaphoreEx(poParent.hsemPointer());
|
|
GreReleaseSemaphoreEx(poParent.hsemDevLock());
|
|
GreReleaseSemaphoreEx(ghsemShareDevLock);
|
|
|
|
if (!bSuccess)
|
|
{
|
|
//
|
|
// We have to undo our 'GreSuspendDirectDraw' call:
|
|
//
|
|
|
|
GreResumeDirectDraw(pmdev->hdevParent, FALSE);
|
|
}
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvDisableMDEV: Leave\n"));
|
|
|
|
return(bSuccess);
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
* DrvDestroyMDEV
|
|
*
|
|
* Deletes a display MDEV.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
VOID
|
|
DrvDestroyMDEV(
|
|
PMDEV pmdev
|
|
)
|
|
{
|
|
GDIFunctionID(DrvDestroyMDEV);
|
|
|
|
ULONG i;
|
|
|
|
// WinBug #306290 2-8-2001 jasonha STRESS: GDI: Two PDEVs using same semaphore as their DevLock
|
|
// WinBug #24003 7-24-2000 jasonha MULTIDISPLAY: Deadlock in DrvDestroyMDEV
|
|
// ghsemDriverManagement used to be held throughout DrvDestroyMDEV
|
|
// even when calling vUnreferencePdev, which might acquire a device
|
|
// lock. This would have been ok if the device had a unique device
|
|
// lock, but when changing from multiple displays to one display, the
|
|
// shared device lock is retained by the PDEVs no being freed. If the
|
|
// new primary display grabs the device lock and then tries to grab
|
|
// ghsemDriverMgmt as in GreCreateDisplayDC we could deadlock.
|
|
// ghsemDriverMgmt only needs to be held during accesses to cPdevOpenRefs.
|
|
|
|
TRACE_INIT(("\n\nDrv_Trace: DrvDestroyMDEV: Enter\n\n"));
|
|
|
|
for (i = 0; i < pmdev->chdev; i++)
|
|
{
|
|
PDEVOBJ po(pmdev->Dev[i].hdev);
|
|
|
|
// !!! What about outstanding bitmaps?
|
|
|
|
GreAcquireSemaphoreEx(ghsemDriverMgmt, SEMORDER_DRIVERMGMT, NULL);
|
|
|
|
ASSERTGDI(!po.bDeleted(), "Deleting an already-deleted PDEV");
|
|
|
|
// Decrease the OpenRefCount which will
|
|
// mark the PDEV as being deleted so that we can also stop DrvEscape
|
|
// calls from going down (ExtEscape ignores the 'bDisabled()' flag):
|
|
|
|
if (--po.ppdev->cPdevOpenRefs == 0)
|
|
{
|
|
// The PDEV should already be disabled.
|
|
// Otherwise, we have no way of detecting whether or not we are just
|
|
// reducing the refcount on the PDEV or not.
|
|
|
|
if (G_fConsole) {
|
|
ASSERTGDI(po.bDisabled(), "Deleting an enabled PDEV");
|
|
}
|
|
}
|
|
|
|
GreReleaseSemaphoreEx(ghsemDriverMgmt);
|
|
|
|
// Once all the DCs open specifically on the PDEV (if there are any)
|
|
// are destroyed, free the PDEV:
|
|
|
|
po.vUnreferencePdev();
|
|
}
|
|
|
|
// If this is multimon system, destroy parent. Otherwise
|
|
// just skip here, since parent is same as its children
|
|
// in signle monitor system.
|
|
|
|
if (pmdev->chdev > 1)
|
|
{
|
|
PDEVOBJ po(pmdev->hdevParent);
|
|
|
|
// !!! What about outstanding bitmaps?
|
|
|
|
GreAcquireSemaphoreEx(ghsemDriverMgmt, SEMORDER_DRIVERMGMT, NULL);
|
|
|
|
ASSERTGDI(!po.bDeleted(), "Deleting an already-deleted Parent PDEV");
|
|
|
|
// Decrease the OpenRefCount which will
|
|
// mark the PDEV as being deleted so that we can also stop DrvEscape
|
|
// calls from going down (ExtEscape ignores the 'bDisabled()' flag):
|
|
|
|
if (--po.ppdev->cPdevOpenRefs == 0)
|
|
{
|
|
// The PDEV should already be disabled.
|
|
// Otherwise, we have no way of detecting whether or not we are just
|
|
// reducing the refcount on the PDEV or not.
|
|
|
|
if (G_fConsole) {
|
|
ASSERTGDI(po.bDisabled(), "Deleting an enabled Parent PDEV");
|
|
}
|
|
}
|
|
|
|
GreReleaseSemaphoreEx(ghsemDriverMgmt);
|
|
|
|
// Once all the DCs open specifically on the PDEV (if there are any)
|
|
// are destroyed, free the PDEV:
|
|
|
|
po.vUnreferencePdev();
|
|
}
|
|
|
|
TRACE_INIT(("\n\nDrv_Trace: DrvDestroyMDEV: Leave\n\n"));
|
|
}
|
|
|
|
/******************************************************************************
|
|
*
|
|
* DrvBackoutMDEV
|
|
*
|
|
* This routine basically undoes all changes made by a ChangeDisplaySettings
|
|
* call.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
VOID
|
|
DrvBackoutMDEV(
|
|
PMDEV pmdev)
|
|
{
|
|
GDIFunctionID(DrvBackoutMDEV);
|
|
|
|
ULONG i;
|
|
HDEV hdev;
|
|
|
|
//
|
|
// First disable all the HDEVs we created.
|
|
//
|
|
|
|
for (i = 0; i < pmdev->chdev; i++)
|
|
{
|
|
hdev = pmdev->Dev[i].hdev;
|
|
PDEVOBJ po(hdev);
|
|
|
|
if (po.ppdev->cPdevOpenRefs == 1)
|
|
{
|
|
if (!DrvDisableDisplay(hdev, FALSE))
|
|
{
|
|
ASSERTGDI(FALSE, "Failed Undoing CreateMDEV - we are hosed !!!\n");
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remove the references so this object can go away
|
|
//
|
|
|
|
GreAcquireSemaphoreEx(ghsemDriverMgmt, SEMORDER_DRIVERMGMT, NULL);
|
|
|
|
po.ppdev->cPdevOpenRefs--;
|
|
po.vUnreferencePdev();
|
|
|
|
GreReleaseSemaphoreEx(ghsemDriverMgmt);
|
|
}
|
|
|
|
//
|
|
// Restore the old HDEVs
|
|
//
|
|
|
|
for (i = 0; i < pmdev->chdev; i++)
|
|
{
|
|
if ((hdev = pmdev->Dev[i].Reserved) != NULL)
|
|
{
|
|
PDEVOBJ po(hdev);
|
|
|
|
//
|
|
// Reenable the display
|
|
//
|
|
|
|
if (po.ppdev->cPdevOpenRefs == 1)
|
|
{
|
|
if (!DrvEnableDisplay(hdev))
|
|
{
|
|
ASSERTGDI(FALSE, "Failed Undoing CreateMDEV - we are hosed !!!\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// hCreateHDEV
|
|
//
|
|
// Creates a PDEV that the window manager will use to open display DCs.
|
|
// This call is made by the window manager to open a new display surface
|
|
// for use. Any number of display surfaces may be open at one time.
|
|
//
|
|
// pDesktopId - Unique identifier to determine the Desktop the PDEV
|
|
// will be associated with.
|
|
//
|
|
// flags
|
|
//
|
|
// GCH_DEFAULT_DISPLAY
|
|
// - Whether or not this is the inital display device (on
|
|
// which the default bitmap will be located).
|
|
//
|
|
// GCH_DDML
|
|
// - The name of the display is the DDML entrypoint
|
|
//
|
|
// GCH_PANNING
|
|
// - The name of the display is the PANNING entrypoint
|
|
//
|
|
//*****************************************************************************
|
|
|
|
// !!! should be partly incorporate in PDEVOBJ::PDEVOBJ since many fields
|
|
// !!! are destroyed by that destructor
|
|
|
|
HDEV
|
|
hCreateHDEV(
|
|
PGRAPHICS_DEVICE PhysDisp,
|
|
PDRV_NAMES lpDisplayNames,
|
|
PDEVMODEW pdriv,
|
|
PVOID pDesktopId,
|
|
DWORD dwOverride,
|
|
DWORD dwAccelLevel,
|
|
BOOL bNoDisable,
|
|
FLONG flags,
|
|
HDEV *phDevDisabled
|
|
)
|
|
{
|
|
GDIFunctionID(hCreateHDEV);
|
|
|
|
ULONG i;
|
|
PDEV *ppdev;
|
|
PDEV *ppdevMatch = NULL;
|
|
PLDEV pldev;
|
|
BOOL bError = FALSE;
|
|
|
|
//
|
|
// Search through the entire PDEV cache to see if we have PDEVs on the
|
|
// same device. If any such a PDEV is active, we need to disable it.
|
|
//
|
|
// All see if a PDEV matches the given mode and is for the same device.
|
|
// If so, simply return that.
|
|
//
|
|
// To be a Match, the PDEV has to match:
|
|
// - The device
|
|
// - The devmode (except for position)
|
|
// - The desktop on which it is located
|
|
//
|
|
|
|
|
|
ASSERTGDI(lpDisplayNames != NULL, "NULL name to hCreateHDEV\n");
|
|
|
|
#if !defined(_GDIPLUS_)
|
|
ASSERTGDI(PhysDisp != NULL, "NULL physdisp to hCreateHDEV\n");
|
|
#endif
|
|
|
|
*phDevDisabled = NULL;
|
|
|
|
if (PhysDisp != (PGRAPHICS_DEVICE) DDML_DRIVER)
|
|
{
|
|
if (pdriv == NULL)
|
|
return NULL;
|
|
|
|
GreAcquireSemaphoreEx(ghsemDriverMgmt, SEMORDER_DRIVERMGMT, NULL);
|
|
|
|
for (ppdev = gppdevList; ppdev != NULL; ppdev = ppdev->ppdevNext)
|
|
{
|
|
PDEVOBJ po((HDEV) ppdev);
|
|
PGRAPHICS_DEVICE pGraphicsDevice = po.ppdev->pGraphicsDevice;
|
|
|
|
//
|
|
// Check if we are on the same actual device.
|
|
// We have to check the VGA device also, in case we are loading
|
|
// the S3 driver dynamically, and the main PhysDisp became a
|
|
// child physdisp on the fly.
|
|
//
|
|
// The VGA check is complex due to the following scenario:
|
|
// - Machine boots in VGA, so the pdev is on the VGA device.
|
|
// - The C&T driver is loaded on the fly, so the pVgaDevice becomes
|
|
// the C&T driver.
|
|
// - We change the mode on the fly, so now we would like to diable
|
|
// the vga pdev. -->> check if our device and the pdev are both
|
|
// on a VGA device.
|
|
//
|
|
|
|
if ((pGraphicsDevice != NULL) &&
|
|
(pGraphicsDevice != (PGRAPHICS_DEVICE) DDML_DRIVER) &&
|
|
((PhysDisp == pGraphicsDevice) ||
|
|
(PhysDisp->pVgaDevice && pGraphicsDevice->pVgaDevice)))
|
|
{
|
|
po.ppdev->cPdevRefs++;
|
|
GreReleaseSemaphoreEx(ghsemDriverMgmt);
|
|
GreAcquireSemaphoreEx(ghsemShareDevLock, SEMORDER_SHAREDEVLOCK, NULL);
|
|
GreAcquireSemaphoreEx(po.hsemDevLock(), SEMORDER_DEVLOCK, NULL);
|
|
GreEnterMonitoredSection(po.ppdev, WD_DEVLOCK);
|
|
|
|
//
|
|
// If this is the matching device, just remember it here because
|
|
// we have to check for all active PDEVs.
|
|
// This also allows us to not disable\reenable the same device
|
|
// if we were not switching.
|
|
//
|
|
// But for Dualview, always reset the mode
|
|
//
|
|
|
|
if ((((PhysDisp->stateFlags & DISPLAY_DEVICE_DUALVIEW) == 0) ||
|
|
(!gbInvalidateDualView) ||
|
|
(bNoDisable == TRUE)) &&
|
|
(!(po.bCloneDriver())) && /* don't pick clone pdev from cache */
|
|
(po.ppdev->pDesktopId == pDesktopId) &&
|
|
(po.ppdev->dwDriverCapableOverride == dwOverride) &&
|
|
(po.ppdev->dwDriverAccelerationLevel == dwAccelLevel) &&
|
|
RtlEqualMemory(pdriv, po.ppdev->ppdevDevmode, sizeof(DEVMODEW))
|
|
)
|
|
{
|
|
ASSERTGDI(ppdevMatch == NULL,
|
|
"Multiple cached PDEV matching the current mode.\n");
|
|
|
|
po.vReferencePdev();
|
|
ppdevMatch = ppdev;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Don't disable the PDEV for an independant desktop open
|
|
//
|
|
|
|
if (bNoDisable == TRUE)
|
|
{
|
|
bError = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If there is an enabled PDEV in this same PhysDisp, then
|
|
// we must disable it.
|
|
// Only one HDEV per device at any one time.
|
|
//
|
|
// There may be multiple active in the case where outstanding
|
|
// Opens have been made (multiple display case only).
|
|
//
|
|
|
|
if (po.bDisabled() == FALSE)
|
|
{
|
|
ASSERTGDI(*phDevDisabled == NULL,
|
|
"Multiple active PDEVs on same device\n");
|
|
|
|
TRACE_INIT(("hCreateHDEV: disabling ppdev %08lx\n", ppdev));
|
|
|
|
if (DrvDisableDisplay((HDEV) ppdev, FALSE))
|
|
{
|
|
*phDevDisabled = (HDEV) ppdev;
|
|
}
|
|
else
|
|
{
|
|
bError = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
GreExitMonitoredSection(po.ppdev, WD_DEVLOCK);
|
|
GreReleaseSemaphoreEx(po.hsemDevLock());
|
|
GreReleaseSemaphoreEx(ghsemShareDevLock);
|
|
GreAcquireSemaphoreEx(ghsemDriverMgmt, SEMORDER_DRIVERMGMT, NULL);
|
|
po.vUnreferencePdev();
|
|
}
|
|
}
|
|
|
|
GreReleaseSemaphoreEx(ghsemDriverMgmt);
|
|
|
|
if (bError)
|
|
{
|
|
//
|
|
// There was an error trying to disable the device.
|
|
// return NULL;
|
|
//
|
|
|
|
if (ppdevMatch != NULL)
|
|
{
|
|
PDEVOBJ po((HDEV) ppdevMatch);
|
|
|
|
po.vUnreferencePdev();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// If we found a match, reenable and refcount the device, as necessary.
|
|
//
|
|
|
|
if (ppdevMatch)
|
|
{
|
|
TRACE_INIT(("hCreateHDEV: Found a ppdev cache match\n"));
|
|
|
|
PDEVOBJ po((HDEV) ppdevMatch);
|
|
|
|
//
|
|
// Increment the Open count of the device
|
|
//
|
|
|
|
GreAcquireSemaphoreEx(ghsemShareDevLock, SEMORDER_SHAREDEVLOCK, NULL);
|
|
GreAcquireSemaphoreEx(po.hsemDevLock(), SEMORDER_DEVLOCK, NULL);
|
|
|
|
GreAcquireSemaphoreEx(ghsemDriverMgmt, SEMORDER_DRIVERMGMT, NULL);
|
|
|
|
po.ppdev->cPdevOpenRefs++;
|
|
|
|
GreReleaseSemaphoreEx(ghsemDriverMgmt);
|
|
|
|
GreEnterMonitoredSection(po.ppdev, WD_DEVLOCK);
|
|
|
|
//
|
|
// Check for Software panning modes.
|
|
//
|
|
|
|
//
|
|
// If the Device was disabled, just reenable it.
|
|
//
|
|
|
|
if (po.bDisabled())
|
|
{
|
|
ASSERTGDI(po.ppdev->cPdevOpenRefs == 1,
|
|
"Inconsistent Open count on disabled PDEV\n");
|
|
|
|
DrvEnableDisplay((HDEV) ppdevMatch);
|
|
}
|
|
|
|
//
|
|
// Reset the offset appropriately.
|
|
//
|
|
|
|
GreExitMonitoredSection(po.ppdev, WD_DEVLOCK);
|
|
GreReleaseSemaphoreEx(po.hsemDevLock());
|
|
GreReleaseSemaphoreEx(ghsemShareDevLock);
|
|
|
|
return((HDEV) ppdevMatch);
|
|
}
|
|
}
|
|
|
|
//
|
|
// No existing PDEV in our list. Let's create a new one.
|
|
//
|
|
|
|
if (lpDisplayNames == NULL)
|
|
{
|
|
WARNING(("Drv_Trace: LoadDisplayDriver: Display driver list was not present.\n"));
|
|
}
|
|
|
|
//
|
|
// Make a fake DC so we can pass in the needed info to vInitBrush.
|
|
// We use allocation instead of stack since sizeof(DC) is about 1.5k
|
|
// WINBUG 393269
|
|
//
|
|
|
|
DC *pdc = (DC*)PALLOCMEM(sizeof(DC), 'pmtG');
|
|
|
|
if (pdc == NULL)
|
|
{
|
|
WARNING("hCreateHDEV: DC memory allocation failed\n");
|
|
goto Exit;
|
|
}
|
|
|
|
for (i = 0; i < lpDisplayNames->cNames; i++)
|
|
{
|
|
//
|
|
// Load the display driver image.
|
|
//
|
|
|
|
switch (flags)
|
|
{
|
|
case GCH_DEFAULT_DISPLAY:
|
|
|
|
pldev = ldevLoadDriver(lpDisplayNames->D[i].lpDisplayName,
|
|
LDEV_DEVICE_DISPLAY);
|
|
break;
|
|
|
|
case GCH_DDML:
|
|
|
|
pldev = ldevLoadInternal((PFN)(lpDisplayNames->D[i].lpDisplayName),
|
|
LDEV_DEVICE_META);
|
|
break;
|
|
|
|
case GCH_GDIPLUS_DISPLAY:
|
|
|
|
pldev = ldevLoadInternal((PFN)(lpDisplayNames->D[i].lpDisplayName),
|
|
LDEV_DEVICE_META);
|
|
break;
|
|
|
|
case GCH_MIRRORING:
|
|
|
|
pldev = ldevLoadDriver(lpDisplayNames->D[i].lpDisplayName,
|
|
LDEV_DEVICE_MIRROR);
|
|
break;
|
|
|
|
case GCH_PANNING:
|
|
|
|
ASSERTGDI(FALSE, "Software Panning not implemented yet\n");
|
|
pldev = NULL;
|
|
break;
|
|
|
|
default:
|
|
|
|
ASSERTGDI(FALSE, "Unknown driver type\n");
|
|
pldev = NULL;
|
|
break;
|
|
}
|
|
|
|
if (pldev == NULL)
|
|
{
|
|
TRACE_INIT(("hCreateHDEV: failed ldevLoadDriver\n"));
|
|
}
|
|
else
|
|
{
|
|
bSetDeviceSessionUsage(PhysDisp, TRUE);
|
|
|
|
PDEVOBJ po(pldev,
|
|
pdriv,
|
|
NULL, // no logical address
|
|
NULL, // no data file
|
|
lpDisplayNames->D[i].lpDisplayName, // device name is the display driver name
|
|
// necessary for hook drivers.
|
|
lpDisplayNames->D[i].hDriver,
|
|
NULL, // no remote typeone info (this isn't font driver)
|
|
0, // prmr.bValid() ? prmr.GdiInfo() : NULL,
|
|
0, // prmr.bValid() ? prmr.pdevinfo() : NULL
|
|
FALSE, // not UMPD driver
|
|
dwOverride, // Capable Override
|
|
dwAccelLevel); // acceleration level.
|
|
|
|
if (!po.bValid())
|
|
{
|
|
TRACE_INIT(("hCreateHDEV: PDEVOBJ::po failed\n"));
|
|
bSetDeviceSessionUsage(PhysDisp, FALSE);
|
|
ldevUnloadImage(pldev);
|
|
}
|
|
else
|
|
{
|
|
|
|
#ifdef DDI_WATCHDOG
|
|
|
|
//
|
|
// Watchdog objects require DEVICE_OBJECT now and we are associating DEVICE_OBJECT
|
|
// with PDEV here (i.e. we don't know DEVICE_OBJECT in PDEVOBJ::PDEVOBJ).
|
|
// Beacuse of this we have to delay creation of watchdog objects till we get here.
|
|
// It would be nice to keep all this stuff in PDEVOBJ::PDEVOBJ constructor.
|
|
//
|
|
|
|
//
|
|
// Create watchdogs for display or mirror device.
|
|
//
|
|
|
|
if ((po.ppdev->pldev->ldevType == LDEV_DEVICE_DISPLAY) ||
|
|
(po.ppdev->pldev->ldevType == LDEV_DEVICE_MIRROR))
|
|
{
|
|
if ((NULL == po.ppdev->pWatchdogData) && PhysDisp)
|
|
{
|
|
PDEVICE_OBJECT pDeviceObject;
|
|
|
|
pDeviceObject = (PDEVICE_OBJECT)(PhysDisp->pDeviceHandle);
|
|
|
|
if (pDeviceObject)
|
|
{
|
|
po.ppdev->pWatchdogData = GreCreateWatchdogs(pDeviceObject,
|
|
WD_NUMBER,
|
|
DDI_TIMEOUT,
|
|
WdDdiWatchdogDpcCallback,
|
|
lpDisplayNames->D[i].lpDisplayName,
|
|
lpDisplayNames->D[i].hDriver,
|
|
&gpldevDrivers);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // DDI_WATCHDOG
|
|
|
|
TRACE_INIT(("hCreateHDEV: about to call pr:bMakeSurface\n"));
|
|
|
|
if (po.bMakeSurface())
|
|
{
|
|
#if DBG
|
|
//
|
|
// Check the surface created by driver is same as what we want.
|
|
//
|
|
|
|
if (po.pSurface() && pdriv && (flags == GCH_DEFAULT_DISPLAY))
|
|
{
|
|
if ((pdriv->dmPelsWidth != (ULONG) po.pSurface()->sizl().cx) ||
|
|
(pdriv->dmPelsHeight != (ULONG) po.pSurface()->sizl().cy))
|
|
{
|
|
DbgPrint("hCreateHDEV() - DEVMODE %d,%d, SURFACE %d,%d\n",
|
|
pdriv->dmPelsWidth, pdriv->dmPelsHeight,
|
|
po.pSurface()->sizl().cx, po.pSurface()->sizl().cy);
|
|
}
|
|
}
|
|
#endif // DBG
|
|
|
|
//
|
|
// Realize the Gray pattern brush used for drag rectangles.
|
|
//
|
|
|
|
po.pbo()->vInit();
|
|
|
|
PBRUSH pbrGrayPattern;
|
|
|
|
pbrGrayPattern = (PBRUSH)HmgShareCheckLock((HOBJ)ghbrGrayPattern,
|
|
BRUSH_TYPE);
|
|
pdc->pDCAttr = &pdc->dcattr;
|
|
pdc->crTextClr(0x00000000);
|
|
pdc->crBackClr(0x00FFFFFF);
|
|
pdc->lIcmMode(DC_ICM_OFF);
|
|
pdc->hcmXform(NULL);
|
|
po.pbo()->vInitBrush(pdc,
|
|
pbrGrayPattern,
|
|
(XEPALOBJ) ppalDefault,
|
|
(XEPALOBJ) po.ppdev->pSurface->ppal(),
|
|
po.ppdev->pSurface);
|
|
|
|
DEC_SHARE_REF_CNT_LAZY0 (pbrGrayPattern);
|
|
|
|
//
|
|
// Now set the global default bitmaps pdev to equal
|
|
// that of our global display device.
|
|
//
|
|
|
|
PSURFACE pSurfDefault = SURFACE::pdibDefault;
|
|
|
|
if (pSurfDefault->hdev() == NULL)
|
|
{
|
|
pSurfDefault->hdev(po.hdev());
|
|
}
|
|
|
|
po.ppdev->pGraphicsDevice = PhysDisp;
|
|
po.ppdev->pDesktopId = pDesktopId;
|
|
|
|
//
|
|
// Keep the DEVMODE, if this is a real display device
|
|
//
|
|
|
|
if ((flags != GCH_DDML) && (flags != GCH_GDIPLUS_DISPLAY))
|
|
{
|
|
po.ppdev->ppdevDevmode = (PDEVMODEW)
|
|
PALLOCNOZ(pdriv->dmSize + pdriv->dmDriverExtra,
|
|
GDITAG_DEVMODE);
|
|
|
|
if (po.ppdev->ppdevDevmode)
|
|
{
|
|
RtlMoveMemory(po.ppdev->ppdevDevmode,
|
|
pdriv,
|
|
pdriv->dmSize + pdriv->dmDriverExtra);
|
|
|
|
//
|
|
// At this moment, this device must be attached. DM_POSITION indicates that
|
|
//
|
|
po.ppdev->ppdevDevmode->dmFields |= DM_POSITION;
|
|
DrvUpdateAttachFlag(PhysDisp, 1);
|
|
}
|
|
else
|
|
{
|
|
bError = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Mark the PDEV as now being ready for use. This has to
|
|
// be one of the last steps, because GCAPS2_SYNCFLUSH and
|
|
// GCAPS2_SYNCTIMER asynchronously loop through the PDEV
|
|
// list and call the driver if 'bDisabled' is not set.
|
|
//
|
|
|
|
po.bDisabled(FALSE);
|
|
|
|
//
|
|
// Profile driver
|
|
//
|
|
// We can not profile meta (= DDML) driver, and while we
|
|
// are creating meta driver, we don't ssync'ed devlock
|
|
// with its children, so we can not call anything which
|
|
// will dispatch to its children.
|
|
//
|
|
|
|
if (!po.bMetaDriver())
|
|
{
|
|
po.vProfileDriver();
|
|
}
|
|
|
|
if (bError)
|
|
{
|
|
// There was an error of memory allocation.
|
|
po.vUnreferencePdev();
|
|
break;
|
|
}
|
|
VFREEMEM(pdc);
|
|
return(po.hdev());
|
|
}
|
|
else
|
|
{
|
|
TRACE_INIT(("hCreateHDEV: bMakeSurface failed\n"));
|
|
bSetDeviceSessionUsage(PhysDisp, FALSE);
|
|
}
|
|
|
|
//
|
|
// Destroy everything if something fails.
|
|
// This will remove the Surface, PDEV and image.
|
|
//
|
|
|
|
po.vUnreferencePdev();
|
|
}
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (pdc)
|
|
VFREEMEM(pdc);
|
|
|
|
//
|
|
// There was an error creating the new device.
|
|
// Reenable the old one if we disabled it.
|
|
//
|
|
if (*phDevDisabled)
|
|
{
|
|
if ((PhysDisp->stateFlags & DISPLAY_DEVICE_DUALVIEW) == 0 || !gbInvalidateDualView)
|
|
{
|
|
DrvEnableDisplay(*phDevDisabled);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#if TEXTURE_DEMO
|
|
|
|
DHPDEV TexEnablePDEV(
|
|
DEVMODEW *pdm, // Actually, the HDEV
|
|
LPWSTR pwszLogAddress,
|
|
ULONG cPat,
|
|
HSURF *phsurfPatterns,
|
|
ULONG cjCaps,
|
|
GDIINFO *pdevcaps,
|
|
ULONG cjDevInfo,
|
|
DEVINFO *pdi,
|
|
HDEV hdev,
|
|
LPWSTR pwszDeviceName,
|
|
HANDLE hDriver)
|
|
{
|
|
HDEV hdevParent = (HDEV) pdm;
|
|
|
|
PDEVOBJ poParent(hdevParent);
|
|
PDEVOBJ po(hdev);
|
|
|
|
pdevcaps->ulHorzRes = gcxTexture;
|
|
pdevcaps->ulVertRes = gcyTexture;
|
|
|
|
ghdevTextureParent = poParent.hdev();
|
|
|
|
return(poParent.dhpdev());
|
|
}
|
|
|
|
HSURF TexEnableSurface(DHPDEV dhpdev)
|
|
{
|
|
PDEVOBJ poPrimary(ghdevTextureParent);
|
|
|
|
DEVLOCKOBJ dlo(poPrimary);
|
|
|
|
HSURF hsurf = hsurfCreateCompatibleSurface(poPrimary.hdev(),
|
|
poPrimary.pSurface()->iFormat(),
|
|
poPrimary.bIsPalManaged()
|
|
? NULL
|
|
: (HPALETTE) poPrimary.ppalSurf()->hGet(),
|
|
512,
|
|
512,
|
|
TRUE);
|
|
|
|
return(hsurf);
|
|
}
|
|
|
|
VOID TexDisablePDEV(DHPDEV dhpdev)
|
|
{
|
|
}
|
|
|
|
VOID TexCompletePDEV(
|
|
DHPDEV dhpdev,
|
|
HDEV hdev)
|
|
{
|
|
}
|
|
|
|
VOID TexDisableSurface(DHPDEV dhpdev)
|
|
{
|
|
}
|
|
|
|
typedef struct _DEMOTEXTURE
|
|
{
|
|
HDC hdc;
|
|
ULONG iTexture;
|
|
BOOL bCopy;
|
|
DEMOQUAD Quad;
|
|
} DEMOTEXTURE;
|
|
|
|
BOOL
|
|
TexTexture(
|
|
VOID* pvBuffer,
|
|
ULONG cjBuffer)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
DEMOTEXTURE tex;
|
|
KFLOATING_SAVE fsFpState;
|
|
|
|
if (cjBuffer < sizeof(DEMOTEXTURE))
|
|
{
|
|
WARNING("TexTexture: Buffer too small");
|
|
return(FALSE);
|
|
}
|
|
|
|
tex = *((DEMOTEXTURE*) pvBuffer);
|
|
|
|
if (ghdevTextureParent == NULL)
|
|
{
|
|
WARNING1("TexTexture: Texturing not enabled");
|
|
return(FALSE);
|
|
}
|
|
|
|
if (tex.iTexture >= gcTextures)
|
|
{
|
|
WARNING("TexTexture: Bad texture identifier");
|
|
return(FALSE);
|
|
}
|
|
|
|
if (!NT_SUCCESS(KeSaveFloatingPointState(&fsFpState)))
|
|
{
|
|
WARNING("TexTexture: Couldn't save floating point state");
|
|
return(FALSE);
|
|
}
|
|
|
|
DCOBJ dco(tex.hdc);
|
|
if (dco.bValid())
|
|
{
|
|
DEVLOCKOBJ dlo(dco);
|
|
if (dlo.bValid())
|
|
{
|
|
PDEVOBJ poParent(ghdevTextureParent);
|
|
PDEVOBJ po(gahdevTexture[tex.iTexture]);
|
|
|
|
if (1) // dco.pSurface()->hdev() == po.pSurface()->hdev())
|
|
{
|
|
ECLIPOBJ eco;
|
|
ERECTL erclBig(-10000, -10000, 10000, 10000);
|
|
|
|
eco.vSetup(dco.prgnEffRao(), erclBig);
|
|
if (!eco.erclExclude().bEmpty())
|
|
{
|
|
if (tex.bCopy)
|
|
{
|
|
ERECTL erclDst(0, 0, dco.pdc->sizl().cx, dco.pdc->sizl().cy);
|
|
|
|
PPFNGET(po, CopyBits, dco.pSurface()->flags())
|
|
(dco.pSurface()->pSurfobj(),
|
|
po.pSurface()->pSurfobj(),
|
|
&eco,
|
|
NULL,
|
|
&erclDst,
|
|
(POINTL*) &erclDst);
|
|
bRet = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (PPFNVALID(poParent, DemoTexture))
|
|
{
|
|
PPFNDRV(po, DemoTexture)(dco.pSurface()->pSurfobj(),
|
|
po.pSurface()->pSurfobj(),
|
|
&eco,
|
|
&tex.Quad,
|
|
1);
|
|
bRet = TRUE;
|
|
}
|
|
else
|
|
{
|
|
WARNING("TexTexture: DrvDemoTexture not hooked");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WARNING("TexTexture: Mismatched HDEVs");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WARNING("TexTexture: Bad devlock");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WARNING("TexTexture: Bad DC");
|
|
}
|
|
|
|
KeRestoreFloatingPointState(&fsFpState);
|
|
|
|
return(bRet);
|
|
}
|
|
|
|
HDC
|
|
hdcTexture(
|
|
ULONG iTexture
|
|
)
|
|
{
|
|
HDC hdc;
|
|
HDEV hdev;
|
|
|
|
hdc = 0;
|
|
hdev = 0;
|
|
|
|
if (iTexture == (ULONG) -1)
|
|
{
|
|
hdev = ghdevTextureParent;
|
|
}
|
|
else if (iTexture < gcTextures)
|
|
{
|
|
hdev = gahdevTexture[iTexture];
|
|
}
|
|
|
|
if (hdev != NULL)
|
|
{
|
|
hdc = GreCreateDisplayDC(hdev,
|
|
DCTYPE_DIRECT,
|
|
FALSE);
|
|
}
|
|
else
|
|
{
|
|
WARNING("hdcTexture: Bad parameter");
|
|
}
|
|
|
|
return(hdc);
|
|
}
|
|
|
|
HDEV
|
|
hCreateTextureHDEV(
|
|
HDEV hdevOwner
|
|
)
|
|
{
|
|
PLDEV pldev;
|
|
|
|
PDEVOBJ poOwner(hdevOwner);
|
|
|
|
pldev = (PLDEV) PALLOCMEM(sizeof(LDEV), GDITAG_LDEV);
|
|
if (!pldev)
|
|
{
|
|
WARNING("hCreateTextureHDEV: LDEV alloc failed.");
|
|
return(0);
|
|
}
|
|
|
|
RtlCopyMemory(pldev, poOwner.ppdev->pldev, sizeof(LDEV));
|
|
|
|
pldev->apfn[INDEX_DrvEnablePDEV] = (PFN) TexEnablePDEV;
|
|
pldev->apfn[INDEX_DrvEnableSurface] = (PFN) TexEnableSurface;
|
|
pldev->apfn[INDEX_DrvCompletePDEV] = (PFN) TexCompletePDEV;
|
|
pldev->apfn[INDEX_DrvDisablePDEV] = (PFN) TexDisablePDEV;
|
|
pldev->apfn[INDEX_DrvDisableSurface] = (PFN) TexDisableSurface;
|
|
|
|
pldev->apfn[INDEX_DrvAssertMode] = NULL;
|
|
pldev->apfn[INDEX_DrvCreateDeviceBitmap] = NULL;
|
|
pldev->apfn[INDEX_DrvDeleteDeviceBitmap] = NULL;
|
|
pldev->apfn[INDEX_DrvSetPalette] = NULL;
|
|
pldev->apfn[INDEX_DrvSetPointerShape] = NULL;
|
|
pldev->apfn[INDEX_DrvSaveScreenBits] = NULL;
|
|
pldev->apfn[INDEX_DrvIcmSetDeviceGammaRamp] = NULL;
|
|
pldev->apfn[INDEX_DrvGetDirectDrawInfo] = NULL;
|
|
|
|
PDEVOBJ po(pldev,
|
|
(PDEVMODEW) hdevOwner,
|
|
NULL, // no logical address
|
|
NULL, // no data file
|
|
NULL, // device name is the display driver name
|
|
// necessary for hook drivers.
|
|
NULL, // driver handle
|
|
NULL,
|
|
poOwner.GdiInfo(),
|
|
poOwner.pdevinfo());
|
|
|
|
if (!po.bValid())
|
|
{
|
|
WARNING("hCreateTextureHDEV failed");
|
|
return(0);
|
|
}
|
|
else
|
|
{
|
|
TRACE_INIT(("hCreateTextureHDEV: about to call pr:bMakeSurface\n"));
|
|
|
|
if (po.bMakeSurface())
|
|
{
|
|
//
|
|
// Make a fake DC so we can pass in the needed info to vInitBrush.
|
|
// Watch out that this is a bit big (about 1k of stack space).
|
|
//
|
|
|
|
DC *pdc = (DC*)PALLOCMEM(sizeof(DC), 'pmtG');
|
|
|
|
if (pdc == NULL)
|
|
{
|
|
po.vUnreferencePdev();
|
|
WARNING("hCreateTextureHDEV: DC memory allocation failed\n");
|
|
return(0);
|
|
}
|
|
|
|
//
|
|
// Realize the gray pattern brush used for drag rectangles.
|
|
//
|
|
|
|
po.pbo()->vInit();
|
|
|
|
PBRUSH pbrGrayPattern;
|
|
|
|
pbrGrayPattern = (PBRUSH)HmgShareCheckLock((HOBJ)ghbrGrayPattern,
|
|
BRUSH_TYPE);
|
|
|
|
pdc->pDCAttr = &pdc->dcattr;
|
|
|
|
pdc->crTextClr(0x00000000);
|
|
pdc->crBackClr(0x00FFFFFF);
|
|
|
|
pdc->lIcmMode(DC_ICM_OFF);
|
|
|
|
pdc->hcmXform(NULL);
|
|
|
|
po.pbo()->vInitBrush(pdc,
|
|
pbrGrayPattern,
|
|
(XEPALOBJ) ppalDefault,
|
|
(XEPALOBJ) po.ppdev->pSurface->ppal(),
|
|
po.ppdev->pSurface);
|
|
|
|
DEC_SHARE_REF_CNT_LAZY0 (pbrGrayPattern);
|
|
|
|
//
|
|
// Now set the global default bitmaps pdev to equal
|
|
// that of our global display device.
|
|
//
|
|
|
|
PSURFACE pSurfDefault = SURFACE::pdibDefault;
|
|
|
|
if (pSurfDefault->hdev() == NULL)
|
|
{
|
|
pSurfDefault->hdev(po.hdev());
|
|
}
|
|
|
|
po.ppdev->pGraphicsDevice = poOwner.ppdev->pGraphicsDevice;
|
|
po.ppdev->pDesktopId = poOwner.ppdev->pDesktopId;
|
|
|
|
po.bDisabled(FALSE);
|
|
|
|
po.vProfileDriver();
|
|
|
|
VFREEMEM(pDC);
|
|
|
|
return(po.hdev());
|
|
}
|
|
else
|
|
{
|
|
TRACE_INIT(("hCreateHDEV: bMakeSurface failed\n"));
|
|
}
|
|
|
|
//
|
|
// Destroy everything if something fails.
|
|
// This will remove the Surface, PDEV and image.
|
|
//
|
|
|
|
po.vUnreferencePdev();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PMDEV
|
|
pmdevSetupTextureDemo(
|
|
PMDEV pmdev)
|
|
{
|
|
ULONG i;
|
|
|
|
if ((gbTexture) && (pmdev) && (pmdev->chdev == 1))
|
|
{
|
|
PMDEV pmdevTmp;
|
|
|
|
//
|
|
// We allocate and copy too much, but we don't care ...
|
|
//
|
|
|
|
pmdevTmp = (PMDEV) PALLOCNOZ(sizeof(MDEV) * (pmdev->chdev + 8),
|
|
GDITAG_DRVSUP);
|
|
|
|
if (pmdevTmp)
|
|
{
|
|
PDEVOBJ poPrimary(pmdev->Dev[0].hdev);
|
|
|
|
//
|
|
// Copy the MDEV list
|
|
//
|
|
|
|
RtlMoveMemory(pmdevTmp, pmdev, sizeof(MDEV) * pmdev->chdev);
|
|
|
|
//
|
|
// Position the surfaces consecutively to the right of the primary.
|
|
//
|
|
|
|
LONG xLeft = pmdev->Dev[0].rect.left;
|
|
LONG yTop = pmdev->Dev[0].rect.bottom;
|
|
|
|
//
|
|
// Hard code them to 512x512.
|
|
//
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
//
|
|
// Create a clone of the PDEV.
|
|
//
|
|
|
|
HDEV hdevTexture = hCreateTextureHDEV(poPrimary.hdev());
|
|
if (!hdevTexture)
|
|
{
|
|
//
|
|
// Delete all the hdevs we have created. and restore the old
|
|
// ones.
|
|
//
|
|
|
|
DrvBackoutMDEV(pmdev);
|
|
VFREEMEM(pmdev);
|
|
pmdev = NULL;
|
|
return(pmdev);
|
|
}
|
|
|
|
PDEVOBJ poTexture(hdevTexture);
|
|
|
|
//
|
|
// Point the surface's HDEV to its true owner, so that
|
|
// software cursors work. I hope.
|
|
//
|
|
|
|
poTexture.pSurface()->hdev(hdevTexture);
|
|
|
|
gahdevTexture[i] = hdevTexture;
|
|
|
|
poTexture.ppdev->ptlOrigin.x = xLeft;
|
|
poTexture.ppdev->ptlOrigin.y = yTop;
|
|
|
|
gcxTexture = 512;
|
|
gcyTexture = 512;
|
|
|
|
pmdevTmp->Dev[1 + i].hdev = hdevTexture;
|
|
pmdevTmp->Dev[1 + i].rect.left = xLeft;
|
|
pmdevTmp->Dev[1 + i].rect.top = yTop;
|
|
pmdevTmp->Dev[1 + i].rect.right = xLeft + gcxTexture;
|
|
pmdevTmp->Dev[1 + i].rect.bottom = yTop + gcyTexture;
|
|
pmdevTmp->Dev[1 + i].Reserved = NULL;
|
|
|
|
pmdevTmp->chdev++;
|
|
gcTextures++;
|
|
|
|
yTop += gcyTexture;
|
|
}
|
|
|
|
VFREEMEM(pmdev);
|
|
|
|
pmdev = pmdevTmp;
|
|
}
|
|
else
|
|
{
|
|
VFREEMEM(pmdevTmp);
|
|
}
|
|
}
|
|
|
|
return(pmdev);
|
|
}
|
|
|
|
#endif // TEXTURE_DEMO
|
|
|
|
/***************************************************************************\
|
|
* DrvGetHDEV
|
|
*
|
|
* routine to find out HDEV for specified device name
|
|
*
|
|
* 01-Oct-1997 hideyukn Created
|
|
\***************************************************************************/
|
|
|
|
HDEV DrvGetHDEV(
|
|
PUNICODE_STRING pstrDeviceName)
|
|
{
|
|
GDIFunctionID(DrvGetHDEV);
|
|
|
|
HDEV hdev = NULL;
|
|
|
|
if (pstrDeviceName)
|
|
{
|
|
PGRAPHICS_DEVICE PhysDisp;
|
|
|
|
PhysDisp = DrvGetDeviceFromName(pstrDeviceName, KernelMode);
|
|
|
|
if (PhysDisp)
|
|
{
|
|
PDEV *ppdev;
|
|
PDEV *ppdevDisabled = NULL;
|
|
|
|
GreAcquireSemaphoreEx(ghsemDriverMgmt, SEMORDER_DRIVERMGMT, NULL);
|
|
|
|
for (ppdev = gppdevList; ppdev != NULL; ppdev = ppdev->ppdevNext)
|
|
{
|
|
PDEVOBJ po((HDEV) ppdev);
|
|
|
|
if ((po.ppdev->pGraphicsDevice != 0) &&
|
|
(po.ppdev->pGraphicsDevice == PhysDisp))
|
|
{
|
|
if (!(po.bDisabled()))
|
|
{
|
|
po.ppdev->cPdevRefs++;
|
|
hdev = po.hdev();
|
|
break;
|
|
}
|
|
else if (ppdevDisabled == NULL)
|
|
{
|
|
//
|
|
// We found a matching PDEV but it is disabled. Save
|
|
// a pointer to it; in case we don't find an enabled
|
|
// PDEV we will use this:
|
|
//
|
|
|
|
ppdevDisabled = ppdev;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((ppdev == NULL) && (ppdevDisabled != NULL))
|
|
{
|
|
//
|
|
// We could not find a matching, enabled PDEV but did find a
|
|
// matching disabled PDEV, so just use it:
|
|
//
|
|
|
|
PDEVOBJ po((HDEV) ppdevDisabled);
|
|
|
|
po.ppdev->cPdevRefs++;
|
|
hdev = po.hdev();
|
|
}
|
|
|
|
GreReleaseSemaphoreEx(ghsemDriverMgmt);
|
|
}
|
|
}
|
|
|
|
return hdev;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrvReleaseHDEV
|
|
*
|
|
* routine to release an HDEV for specified device name
|
|
*
|
|
* 02-Mar-1999 a-oking Created
|
|
\***************************************************************************/
|
|
|
|
void DrvReleaseHDEV(
|
|
PUNICODE_STRING pstrDeviceName)
|
|
{
|
|
if (pstrDeviceName)
|
|
{
|
|
PGRAPHICS_DEVICE PhysDisp;
|
|
|
|
PhysDisp = DrvGetDeviceFromName(pstrDeviceName, KernelMode);
|
|
|
|
if (PhysDisp)
|
|
{
|
|
PDEV *ppdev;
|
|
|
|
GreAcquireSemaphoreEx(ghsemDriverMgmt, SEMORDER_DRIVERMGMT, NULL);
|
|
|
|
for (ppdev = gppdevList; ppdev != NULL; ppdev = ppdev->ppdevNext)
|
|
{
|
|
PDEVOBJ po((HDEV) ppdev);
|
|
|
|
if ((po.ppdev->pGraphicsDevice != 0) &&
|
|
(po.ppdev->pGraphicsDevice == PhysDisp))
|
|
{
|
|
po.ppdev->cPdevRefs--;
|
|
break;
|
|
}
|
|
}
|
|
|
|
GreReleaseSemaphoreEx(ghsemDriverMgmt);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* DrvCreateCloneHDEV
|
|
*
|
|
* create clone of passed HDEV.
|
|
*
|
|
* 27-Feb-1998 hideyukn Created
|
|
\***************************************************************************/
|
|
|
|
#define DRV_CLONE_DEREFERENCE_ORG 0x0001
|
|
|
|
HDEV DrvCreateCloneHDEV(
|
|
HDEV hdevOrg,
|
|
ULONG ulFlags)
|
|
{
|
|
GDIFunctionID(DrvCreateCloneHDEV);
|
|
|
|
HDEV hdevClone = NULL;
|
|
|
|
PDEVOBJ pdoOrg(hdevOrg);
|
|
|
|
ASSERTGDI(!pdoOrg.bDisabled(), "DrvCreateCloneHDEV(): hdevOrg must be enabled");
|
|
|
|
// Grab the SPRITELOCK. This is important in case sprites are still
|
|
// hooked at this point because the surface type and flags have been
|
|
// altered by the sprite code, and unless we grab the SPRITELOCK here
|
|
// they will be recorded incorrectly in the SPRITESTATE of the new PDEV
|
|
// during the call to bSpEnableSprites. See bug #266112 for details.
|
|
|
|
SPRITELOCK slock(pdoOrg);
|
|
|
|
// Create clone PDEVOBJ.
|
|
|
|
PDEVOBJ pdoClone(hdevOrg,GCH_CLONE_DISPLAY);
|
|
|
|
if (pdoClone.bValid())
|
|
{
|
|
BOOL bRet;
|
|
|
|
//
|
|
// Allocate a temporary DC so we can pass in the needed info to
|
|
// vInitBrush. We allocate on the heap since this is a big
|
|
// structure.
|
|
//
|
|
|
|
DC *pdc = (DC*)PALLOCMEM(sizeof(DC), 'pmtG');
|
|
|
|
if (pdc == NULL)
|
|
{
|
|
WARNING("DrvCreateCloneHDEV: DC memory allocation failed\n");
|
|
}
|
|
|
|
//
|
|
// Enable Sprites on new PDEV.
|
|
//
|
|
|
|
if ((pdc != NULL) &&
|
|
bSpEnableSprites(pdoClone.hdev()))
|
|
{
|
|
vEnableSynchronize(pdoClone.hdev());
|
|
|
|
// Realize the Gray pattern brush used for drag rectangles.
|
|
|
|
pdc->pDCAttr = &pdc->dcattr;
|
|
pdc->crTextClr(0x00000000);
|
|
pdc->crBackClr(0x00FFFFFF);
|
|
pdc->lIcmMode(DC_ICM_OFF);
|
|
pdc->hcmXform(NULL);
|
|
|
|
PBRUSH pbrGrayPattern = (PBRUSH)HmgShareCheckLock(
|
|
(HOBJ)ghbrGrayPattern,
|
|
BRUSH_TYPE);
|
|
pdoClone.pbo()->vInit();
|
|
pdoClone.pbo()->vInitBrush(pdc,
|
|
pbrGrayPattern,
|
|
(XEPALOBJ)ppalDefault,
|
|
(XEPALOBJ)pdoClone.ppdev->pSurface->ppal(),
|
|
pdoClone.ppdev->pSurface);
|
|
|
|
DEC_SHARE_REF_CNT_LAZY0 (pbrGrayPattern);
|
|
|
|
// Dereference original HDEV, if nessesary.
|
|
|
|
if (ulFlags & DRV_CLONE_DEREFERENCE_ORG)
|
|
{
|
|
GreAcquireSemaphoreEx(ghsemDriverMgmt, SEMORDER_DRIVERMGMT, NULL);
|
|
|
|
ASSERTGDI(pdoOrg.ppdev->cPdevOpenRefs > 1,
|
|
"DrvCreateCloneHDEV: cPdevOpenRefs <= 1\n");
|
|
ASSERTGDI(pdoOrg.ppdev->cPdevRefs > 1,
|
|
"DrvCreateCloneHDEV: cPdevRefs <= 1\n");
|
|
|
|
pdoOrg.ppdev->cPdevOpenRefs--;
|
|
pdoOrg.vUnreferencePdev();
|
|
|
|
GreReleaseSemaphoreEx(ghsemDriverMgmt);
|
|
}
|
|
|
|
// Mirror the status.
|
|
|
|
pdoClone.bDisabled(pdoOrg.bDisabled());
|
|
|
|
// Obtain cloned HDEV handle.
|
|
|
|
hdevClone = pdoClone.hdev();
|
|
}
|
|
else
|
|
{
|
|
pdoClone.vUnreferencePdev();
|
|
}
|
|
|
|
if (NULL != pdc)
|
|
{
|
|
VFREEMEM(pdc);
|
|
}
|
|
}
|
|
|
|
return (hdevClone);
|
|
}
|
|
|
|
BOOL
|
|
GetPrimaryAttachFlags(
|
|
PGRAPHICS_DEVICE PhysDisp,
|
|
PULONG pPrimary,
|
|
PULONG pAttached
|
|
)
|
|
{
|
|
HANDLE hkRegistry = NULL;
|
|
ULONG defaultValue = 0;
|
|
USHORT usProtocolType;
|
|
|
|
*pPrimary = *pAttached = 0;
|
|
|
|
//
|
|
// Get the attached and primary data, which is per config (or
|
|
// also global if necessary.
|
|
//
|
|
|
|
RTL_QUERY_REGISTRY_TABLE AttachedQueryTable[] = {
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT, AttachedSettings[0],
|
|
pPrimary, REG_DWORD, &defaultValue, 4},
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT, AttachedSettings[1],
|
|
pAttached, REG_DWORD, &defaultValue, 4},
|
|
{NULL, 0, NULL}
|
|
};
|
|
|
|
if (PhysDisp->stateFlags & DISPLAY_DEVICE_DISCONNECT){
|
|
usProtocolType = PROTOCOL_DISCONNECT;
|
|
} else if (PhysDisp->stateFlags & DISPLAY_DEVICE_REMOTE) {
|
|
usProtocolType = gProtocolType;
|
|
} else {
|
|
usProtocolType = PROTOCOL_CONSOLE;
|
|
}
|
|
|
|
hkRegistry = DrvGetRegistryHandleFromDeviceMap(PhysDisp,
|
|
DispDriverRegHardwareProfile,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
usProtocolType);
|
|
if (hkRegistry)
|
|
{
|
|
RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
|
|
(PWSTR)hkRegistry,
|
|
&AttachedQueryTable[0],
|
|
NULL,
|
|
NULL);
|
|
|
|
ZwCloseKey(hkRegistry);
|
|
}
|
|
else
|
|
{
|
|
hkRegistry = DrvGetRegistryHandleFromDeviceMap(PhysDisp,
|
|
DispDriverRegGlobal,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
usProtocolType);
|
|
if (hkRegistry)
|
|
{
|
|
NTSTATUS ntStatus;
|
|
|
|
ntStatus = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
|
|
(PWSTR)hkRegistry,
|
|
&AttachedQueryTable[0],
|
|
NULL,
|
|
NULL);
|
|
|
|
ZwCloseKey(hkRegistry);
|
|
|
|
if (!NT_SUCCESS(ntStatus))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
switch (gProtocolType) {
|
|
case PROTOCOL_CONSOLE:
|
|
if ((PhysDisp->stateFlags & DISPLAY_DEVICE_DISCONNECT) ||
|
|
(PhysDisp->stateFlags & DISPLAY_DEVICE_REMOTE))
|
|
*pAttached = 0;
|
|
|
|
break;
|
|
|
|
case PROTOCOL_DISCONNECT:
|
|
if (!(PhysDisp->stateFlags & DISPLAY_DEVICE_DISCONNECT)){
|
|
*pAttached = 0;
|
|
} else{
|
|
*pAttached = 1;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (!(PhysDisp->stateFlags & DISPLAY_DEVICE_REMOTE) || (PhysDisp->ProtocolType != gProtocolType)){
|
|
*pAttached = 0;
|
|
} else{
|
|
*pAttached = 1;
|
|
}
|
|
|
|
}
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvCreateMDEV: Display driver is %sprimary on the desktop\n",
|
|
*pPrimary ? "" : "NOT "));
|
|
TRACE_INIT(("Drv_Trace: DrvCreateMDEV: Display driver is %sattached to the desktop\n",
|
|
*pAttached ? "" : "NOT "));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrvCreateMDEV
|
|
*
|
|
* Initialize an MDEV structure
|
|
*
|
|
*
|
|
* andreva Created
|
|
\***************************************************************************/
|
|
|
|
PMDEV
|
|
DrvCreateMDEV(
|
|
PUNICODE_STRING pstrDeviceName,
|
|
LPDEVMODEW lpdevmodeInformation,
|
|
PVOID pDesktopId,
|
|
ULONG ulFlags,
|
|
PMDEV pMdevOrg,
|
|
MODE PreviousMode,
|
|
DWORD PruneFlag,
|
|
BOOL bClosest
|
|
)
|
|
{
|
|
GDIFunctionID(DrvCreateMDEV);
|
|
|
|
NTSTATUS retStatus = STATUS_SUCCESS;
|
|
BOOL bAttachMirroring = FALSE;
|
|
BOOL displayInstalled = FALSE;
|
|
BOOL bLoad;
|
|
PMDEV pmdev;
|
|
BOOL bNoDisable = (ulFlags & GRE_DISP_CREATE_NODISABLE);
|
|
BOOL bModeChange = (pMdevOrg != NULL);
|
|
BOOL bPrune = (PruneFlag != GRE_RAWMODE);
|
|
PGRAPHICS_DEVICE PhysDisp;
|
|
|
|
TRACE_INIT(("\nDrv_Trace: DrvCreateMDEV: Enter\n"));
|
|
|
|
GRAPHICS_STATE GraphicsState = GraphicsStateFull;
|
|
|
|
if (pstrDeviceName)
|
|
{
|
|
//
|
|
// Check if CreateDC on Dualview.
|
|
// Since opening a DualView must affect the other view,
|
|
// so if a Dualview is not opened in orginal MDEV, don't allow it to open
|
|
//
|
|
PhysDisp = DrvGetDeviceFromName(pstrDeviceName, PreviousMode);
|
|
if (PhysDisp && (PhysDisp->stateFlags & DISPLAY_DEVICE_DUALVIEW))
|
|
{
|
|
PPDEV ppdev;
|
|
|
|
for (ppdev = gppdevList; ppdev != NULL; ppdev = ppdev->ppdevNext)
|
|
{
|
|
PDEVOBJ po((HDEV) ppdev);
|
|
if (po.ppdev->pGraphicsDevice == PhysDisp)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (ppdev == NULL)
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// First pass through the loop - find all the devices that should be
|
|
// are atached.
|
|
// If not attached devices, go through the loop again to find a default
|
|
// device.
|
|
// Thirdly - try to find any mirroring devices.
|
|
//
|
|
|
|
//
|
|
// Allocate new MDEV (with zero initialization)
|
|
//
|
|
|
|
pmdev = (PMDEV) PALLOCMEM(sizeof(MDEV), GDITAG_DRVSUP);
|
|
|
|
if (pmdev == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
pmdev->chdev = 0;
|
|
pmdev->pDesktopId = pDesktopId;
|
|
|
|
while (1)
|
|
{
|
|
BOOL bMultiDevice = TRUE;
|
|
HDEV hDev;
|
|
HDEV hDevDisabled;
|
|
PGRAPHICS_DEVICE PhysDispOfDeviceName = NULL;
|
|
DWORD devNum = 0;
|
|
NTSTATUS tmpStatus;
|
|
|
|
HANDLE hkRegistry = NULL;
|
|
ULONG defaultValue = 0;
|
|
ULONG attached = 0;
|
|
ULONG primary = 0;
|
|
PDRV_NAMES lpDisplayNames;
|
|
|
|
while (bMultiDevice &&
|
|
(retStatus == STATUS_SUCCESS))
|
|
{
|
|
PhysDisp = NULL;
|
|
hDev = NULL;
|
|
hDevDisabled = NULL;
|
|
|
|
if (pstrDeviceName && (PhysDispOfDeviceName == NULL))
|
|
{
|
|
PhysDispOfDeviceName = PhysDisp = DrvGetDeviceFromName(pstrDeviceName,
|
|
PreviousMode);
|
|
|
|
//
|
|
// If we are under mode change, and device name specified. it
|
|
// means caller want to change the mode for specified device
|
|
// and leave other device as is. so we will still look for
|
|
// other device in > 2 loops.
|
|
//
|
|
|
|
if (!bModeChange)
|
|
{
|
|
bMultiDevice = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (PhysDispOfDeviceName && bModeChange)
|
|
{
|
|
//
|
|
// We are here when ...
|
|
//
|
|
// + DrvCreateMDEV get called with specific device name,
|
|
// AND
|
|
// + During mode change.
|
|
//
|
|
// so get device from original MDEV instead of enumerate,
|
|
//
|
|
|
|
if (devNum < pMdevOrg->chdev)
|
|
{
|
|
hDev = pMdevOrg->Dev[devNum++].hdev;
|
|
|
|
PDEVOBJ pdo(hDev);
|
|
|
|
PhysDisp = pdo.ppdev->pGraphicsDevice;
|
|
|
|
//
|
|
// If this device is already attached (in 1st loop),
|
|
// don't need to attach again.
|
|
//
|
|
|
|
if (PhysDisp == PhysDispOfDeviceName)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If we are in context of "adding mirroring driver"
|
|
// and this is mirroring
|
|
// device continue to add it, otherwise forget this.
|
|
//
|
|
|
|
if ((PhysDisp->stateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) ?
|
|
(!bAttachMirroring) : bAttachMirroring)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Increment the Open count of the device
|
|
//
|
|
|
|
GreAcquireSemaphoreEx(ghsemShareDevLock, SEMORDER_SHAREDEVLOCK, NULL);
|
|
GreAcquireSemaphoreEx(pdo.hsemDevLock(), SEMORDER_DEVLOCK, NULL);
|
|
|
|
GreAcquireSemaphoreEx(ghsemDriverMgmt, SEMORDER_DRIVERMGMT, NULL);
|
|
|
|
pdo.ppdev->cPdevOpenRefs++;
|
|
pdo.ppdev->cPdevRefs++;
|
|
|
|
GreReleaseSemaphoreEx(ghsemDriverMgmt);
|
|
|
|
GreEnterMonitoredSection(pdo.ppdev, WD_DEVLOCK);
|
|
|
|
//
|
|
// If the Device was disabled, just reenable it.
|
|
// But for dualview, we want to delay the enable until new MDEV is created
|
|
//
|
|
|
|
if (pdo.bDisabled() &&
|
|
(((PhysDisp->stateFlags & DISPLAY_DEVICE_DUALVIEW) == 0) ||
|
|
!gbInvalidateDualView)
|
|
)
|
|
{
|
|
ASSERTGDI(pdo.ppdev->cPdevOpenRefs == 1,
|
|
"Inconsistent Open count on disabled PDEV\n");
|
|
|
|
DrvEnableDisplay((HDEV) pdo.ppdev);
|
|
}
|
|
|
|
GreExitMonitoredSection(pdo.ppdev, WD_DEVLOCK);
|
|
GreReleaseSemaphoreEx(pdo.hsemDevLock());
|
|
GreReleaseSemaphoreEx(ghsemShareDevLock);
|
|
|
|
primary = (PhysDisp->stateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) ? 1 : 0;
|
|
attached = 1; // since already attached.
|
|
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DWORD cCount = 0;
|
|
for (PhysDisp = gpGraphicsDeviceList;
|
|
PhysDisp != NULL;
|
|
PhysDisp = PhysDisp->pNextGraphicsDevice, cCount++)
|
|
{
|
|
if (cCount == devNum)
|
|
break;
|
|
}
|
|
devNum++;
|
|
|
|
//
|
|
// For BaseVideo, on a default boot (as opposed to a test
|
|
// in the applet), just look for the VGA driver.
|
|
//
|
|
|
|
if (gbBaseVideo && PhysDisp)
|
|
{
|
|
PhysDisp = PhysDisp->pVgaDevice;
|
|
|
|
if (PhysDisp == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (PhysDisp == NULL) {
|
|
break;
|
|
}
|
|
|
|
if (PruneFlag == GRE_DEFAULT) {
|
|
bPrune = DrvGetPruneFlag(PhysDisp);
|
|
}
|
|
|
|
ASSERTGDI(PhysDisp, "Can not have NULL PhysDisp here\n");
|
|
|
|
if (!hDev)
|
|
{
|
|
/*****************************************************************
|
|
*****************************************************************
|
|
Get Device Information
|
|
*****************************************************************
|
|
*****************************************************************/
|
|
|
|
TRACE_INIT(("\nDrv_Trace: DrvCreateMDEV: Trying to open device %ws \n", &(PhysDisp->szNtDeviceName[0])));
|
|
|
|
if (!GetPrimaryAttachFlags(PhysDisp, &primary, &attached))
|
|
{
|
|
break;
|
|
}
|
|
|
|
/*****************************************************************
|
|
*****************************************************************
|
|
Load Display Drivers
|
|
*****************************************************************
|
|
*****************************************************************/
|
|
|
|
//
|
|
// Try to open the display driver associated to the kernel driver.
|
|
//
|
|
// We want to do this if we are looking for an attached device (taking
|
|
// into account mirror devices properly) or if we are just looking
|
|
// for any device.
|
|
//
|
|
|
|
if (GraphicsState == GraphicsStateFull)
|
|
{
|
|
bLoad = attached &&
|
|
((PhysDisp->stateFlags &
|
|
DISPLAY_DEVICE_MIRRORING_DRIVER) ?
|
|
bAttachMirroring :
|
|
(!bAttachMirroring));
|
|
}
|
|
else if (GraphicsState == GraphicsStateNoAttach)
|
|
{
|
|
if (PhysDisp->stateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER)
|
|
{
|
|
bLoad = (attached && bAttachMirroring);
|
|
}
|
|
else if (PhysDisp->stateFlags & DISPLAY_DEVICE_DISCONNECT)
|
|
{
|
|
bLoad = FALSE;
|
|
}
|
|
else
|
|
{
|
|
ASSERTGDI(displayInstalled || gProtocolType != PROTOCOL_DISCONNECT,
|
|
"Failed to load disconnected driver while in disconnect mode.\n");
|
|
|
|
bLoad = (!displayInstalled && (gProtocolType != PROTOCOL_DISCONNECT));
|
|
}
|
|
}
|
|
else if (GraphicsState == GraphicsStateAttachDisconnect)
|
|
{
|
|
if (PhysDisp->stateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER)
|
|
{
|
|
bLoad = (attached && bAttachMirroring);
|
|
}
|
|
else
|
|
{
|
|
bLoad = (!displayInstalled);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bLoad = (PhysDisp->stateFlags &
|
|
DISPLAY_DEVICE_MIRRORING_DRIVER) ?
|
|
FALSE :
|
|
(!displayInstalled);
|
|
}
|
|
|
|
if (bLoad &&
|
|
(lpDisplayNames = DrvGetDisplayDriverNames(PhysDisp)))
|
|
{
|
|
PDEVMODEW pdevmodeInformation;
|
|
DEVMODEW sourceDevmodeInformation;
|
|
DWORD dwAccelLevel;
|
|
DWORD dwOverride;
|
|
BOOL uu;
|
|
FLONG flag = (PhysDisp->stateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) ?
|
|
GCH_MIRRORING : GCH_DEFAULT_DISPLAY;
|
|
|
|
if (flag == GCH_DEFAULT_DISPLAY)
|
|
{
|
|
dwOverride = DrvGetDriverCapableOverRide(PhysDisp);
|
|
dwAccelLevel = DrvGetDriverAccelerationsLevel(PhysDisp);
|
|
}
|
|
else
|
|
{
|
|
dwOverride = DRIVER_CAPABLE_ALL;
|
|
dwAccelLevel = 0;
|
|
}
|
|
|
|
//
|
|
// We will try to load the driver using the information in the
|
|
// registry. If it matches perfectly with a mode from the driver -
|
|
// great. If it's a loose match, the we just give a warning.
|
|
//
|
|
// If that does not work, we will want to try the first mode
|
|
// in the list - which we get by matching with 0,0,0
|
|
//
|
|
// If that also fails, we want to boot with the default DEVMODE
|
|
// that we pass to the driver.
|
|
//
|
|
|
|
if (lpdevmodeInformation)
|
|
{
|
|
tmpStatus = DrvProbeAndCaptureDevmode(PhysDisp,
|
|
&pdevmodeInformation,
|
|
&uu,
|
|
lpdevmodeInformation,
|
|
FALSE,
|
|
PreviousMode,
|
|
bPrune,
|
|
bClosest,
|
|
FALSE);
|
|
}
|
|
else
|
|
{
|
|
RtlZeroMemory(&sourceDevmodeInformation, sizeof(DEVMODEW));
|
|
sourceDevmodeInformation.dmSize = sizeof(DEVMODEW);
|
|
|
|
tmpStatus = DrvProbeAndCaptureDevmode(PhysDisp,
|
|
&pdevmodeInformation,
|
|
&uu,
|
|
&sourceDevmodeInformation,
|
|
FALSE,
|
|
KernelMode,
|
|
bPrune,
|
|
bClosest,
|
|
FALSE);
|
|
}
|
|
|
|
if (tmpStatus == STATUS_RECEIVE_PARTIAL)
|
|
{
|
|
DrvLogDisplayDriverEvent(MsgInvalidDisplayMode);
|
|
}
|
|
else if (tmpStatus == STATUS_INVALID_PARAMETER_MIX)
|
|
{
|
|
ASSERTGDI(PhysDisp->stateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER,
|
|
"Only mirror drivers can return this error code\n");
|
|
|
|
//
|
|
// In the case of mirroring, we want to use the same
|
|
// parameters as were provided for the main display.
|
|
// We do this by passing the HDEV of the device we are
|
|
// duplicating which will actually allow the driver to
|
|
// get all the details about the other device
|
|
//
|
|
|
|
PDEVOBJ pdo(pmdev->Dev[0].hdev);
|
|
PDEVMODEW pdm = pdo.ppdev->ppdevDevmode;
|
|
|
|
if (pdevmodeInformation &&
|
|
pdevmodeInformation != &sourceDevmodeInformation)
|
|
{
|
|
VFREEMEM(pdevmodeInformation);
|
|
pdevmodeInformation = NULL;
|
|
}
|
|
tmpStatus = DrvProbeAndCaptureDevmode(PhysDisp,
|
|
&pdevmodeInformation,
|
|
&uu,
|
|
pdm,
|
|
FALSE,
|
|
PreviousMode,
|
|
bPrune,
|
|
bClosest,
|
|
FALSE);
|
|
}
|
|
|
|
//
|
|
// hCreateHDEV will appropriately compare the current mode
|
|
// to the device, and disable the current PDEV if necessary
|
|
//
|
|
|
|
if (NT_SUCCESS(tmpStatus))
|
|
{
|
|
hDev = hCreateHDEV(PhysDisp,
|
|
lpDisplayNames,
|
|
pdevmodeInformation,
|
|
pDesktopId,
|
|
dwOverride,
|
|
dwAccelLevel,
|
|
bNoDisable,
|
|
flag,
|
|
&hDevDisabled);
|
|
}
|
|
|
|
//
|
|
// We failed to load a display driver with this devmode.
|
|
// Try to pick the first valid Devmode, unless the user
|
|
// requested a specific devmode.
|
|
//
|
|
// If it still fails, try 640x480x4 VGA mode. We must get
|
|
// something to set
|
|
//
|
|
|
|
if (!(PhysDisp->stateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) &&
|
|
(lpdevmodeInformation == NULL))
|
|
{
|
|
if (!hDev)
|
|
{
|
|
DrvLogDisplayDriverEvent(MsgInvalidDisplayMode);
|
|
|
|
//
|
|
// Free memory allocated by DrvProbeAndCaptureDevmode
|
|
//
|
|
|
|
if (pdevmodeInformation)
|
|
{
|
|
//
|
|
// Log an error saying the selected color or
|
|
// resolution is invalid.
|
|
//
|
|
|
|
if (pdevmodeInformation->dmBitsPerPel == 0x4)
|
|
{
|
|
DrvLogDisplayDriverEvent(MsgInvalidDisplay16Colors);
|
|
}
|
|
|
|
if (pdevmodeInformation != &sourceDevmodeInformation)
|
|
{
|
|
VFREEMEM(pdevmodeInformation);
|
|
pdevmodeInformation = NULL;
|
|
}
|
|
}
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvCreateMDEV: Trying first DEVMODE\n"));
|
|
|
|
RtlZeroMemory(&sourceDevmodeInformation, sizeof(DEVMODEW));
|
|
sourceDevmodeInformation.dmSize = sizeof(DEVMODEW);
|
|
|
|
if (NT_SUCCESS(DrvProbeAndCaptureDevmode(PhysDisp,
|
|
&pdevmodeInformation,
|
|
&uu,
|
|
&sourceDevmodeInformation,
|
|
TRUE,
|
|
KernelMode,
|
|
bPrune,
|
|
bClosest,
|
|
FALSE)))
|
|
{
|
|
hDev = hCreateHDEV(PhysDisp,
|
|
lpDisplayNames,
|
|
pdevmodeInformation,
|
|
pDesktopId,
|
|
dwOverride,
|
|
dwAccelLevel,
|
|
bNoDisable,
|
|
GCH_DEFAULT_DISPLAY,
|
|
&hDevDisabled);
|
|
|
|
//
|
|
// At last try 640x480x4bpp. With Framebuf driver,
|
|
// 640x480x4 will never be picked by DrvProbeAndCapture().
|
|
// And it's very likely that the mode set can be failed.
|
|
//
|
|
if (hDev == NULL && pdevmodeInformation->dmBitsPerPel != 0x4)
|
|
{
|
|
if (pdevmodeInformation &&
|
|
pdevmodeInformation != &sourceDevmodeInformation)
|
|
{
|
|
VFREEMEM(pdevmodeInformation);
|
|
pdevmodeInformation = NULL;
|
|
}
|
|
RtlZeroMemory(&sourceDevmodeInformation, sizeof(DEVMODEW));
|
|
sourceDevmodeInformation.dmSize = sizeof(DEVMODEW);
|
|
sourceDevmodeInformation.dmBitsPerPel = 0x4;
|
|
sourceDevmodeInformation.dmPelsWidth = 640;
|
|
sourceDevmodeInformation.dmPelsHeight = 480;
|
|
sourceDevmodeInformation.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
|
|
|
|
if (NT_SUCCESS(DrvProbeAndCaptureDevmode(PhysDisp,
|
|
&pdevmodeInformation,
|
|
&uu,
|
|
&sourceDevmodeInformation,
|
|
FALSE,
|
|
KernelMode,
|
|
bPrune,
|
|
bClosest,
|
|
FALSE)))
|
|
{
|
|
hDev = hCreateHDEV(PhysDisp,
|
|
lpDisplayNames,
|
|
pdevmodeInformation,
|
|
pDesktopId,
|
|
dwOverride,
|
|
dwAccelLevel,
|
|
bNoDisable,
|
|
GCH_DEFAULT_DISPLAY,
|
|
&hDevDisabled);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!hDev)
|
|
{
|
|
//
|
|
// If no display driver initialized with the requested
|
|
// settings, put a message in the error log.
|
|
// (unless this was a change display settings call).
|
|
//
|
|
|
|
if (lpdevmodeInformation == NULL)
|
|
{
|
|
DrvLogDisplayDriverEvent(MsgInvalidDisplayDriver);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free memory allocated by DrvProbeAndCaptureDevmode
|
|
//
|
|
|
|
if (pdevmodeInformation &&
|
|
(pdevmodeInformation != &sourceDevmodeInformation))
|
|
{
|
|
VFREEMEM(pdevmodeInformation);
|
|
}
|
|
|
|
VFREEMEM(lpDisplayNames);
|
|
}
|
|
}
|
|
|
|
if (hDev)
|
|
{
|
|
PMDEV pmdevTmp;
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvCreateMDEV: Display Driver Loaded successfully\n"));
|
|
|
|
//
|
|
// We installed a display driver successfully, so we
|
|
// know to exit out of the loop successfully.
|
|
//
|
|
|
|
displayInstalled = TRUE;
|
|
|
|
//
|
|
// Mark this device as being part of the primary device
|
|
//
|
|
|
|
if (primary)
|
|
{
|
|
PhysDisp->stateFlags |= DISPLAY_DEVICE_PRIMARY_DEVICE;
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Otherwise, mask off it, since if this physical display
|
|
// was primary previously, it still keeps it, so need to
|
|
// mask of it.
|
|
//
|
|
|
|
PhysDisp->stateFlags &= ~DISPLAY_DEVICE_PRIMARY_DEVICE;
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// If we haven't found the previously enabled hdev for this device
|
|
// look for it in the original mdev's list.
|
|
//
|
|
|
|
if (hDevDisabled == NULL && pMdevOrg != NULL)
|
|
{
|
|
for (ULONG i = 0; i < pMdevOrg->chdev; i++)
|
|
{
|
|
PDEVOBJ po((HDEV) pMdevOrg->Dev[i].hdev);
|
|
PGRAPHICS_DEVICE pGraphicsDevice = po.ppdev->pGraphicsDevice;
|
|
|
|
if (PhysDisp == pGraphicsDevice)
|
|
{
|
|
hDevDisabled = pMdevOrg->Dev[i].hdev;
|
|
}
|
|
}
|
|
}
|
|
|
|
pmdev->Dev[pmdev->chdev].hdev = hDev;
|
|
pmdev->Dev[pmdev->chdev].Reserved = hDevDisabled;
|
|
pmdev->chdev += 1;
|
|
|
|
pmdevTmp = pmdev;
|
|
|
|
//
|
|
// We allocate and copy too much, but we don't care ...
|
|
//
|
|
|
|
pmdev = (PMDEV) PALLOCMEM(sizeof(MDEV) * (pmdevTmp->chdev + 1),
|
|
GDITAG_DRVSUP);
|
|
|
|
if (pmdev)
|
|
{
|
|
//
|
|
// Compiler bug workaround MoveMemory instead of Copy
|
|
//
|
|
|
|
RtlMoveMemory(pmdev,
|
|
pmdevTmp,
|
|
sizeof(MDEV) * pmdevTmp->chdev);
|
|
|
|
VFREEMEM(pmdevTmp);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We will exit the loop since we had a memory
|
|
// allocation failure.
|
|
//
|
|
|
|
pmdev = pmdevTmp;
|
|
retStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************
|
|
*****************************************************************
|
|
Handle loop exit conditions
|
|
*****************************************************************
|
|
*****************************************************************/
|
|
|
|
//
|
|
// If an error occured, we want to bring back the devices to their
|
|
// normal state.
|
|
//
|
|
|
|
if (!NT_SUCCESS(retStatus))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If there is no attached display drivers, then go to compatibility
|
|
// mode and load any display device except Disconnect driver.
|
|
// If still fails(only applies to local session, check it's headless
|
|
// server and try to load Disconnect driver
|
|
//
|
|
|
|
if (!displayInstalled &&
|
|
(GraphicsState == GraphicsStateFull))
|
|
{
|
|
TRACE_INIT(("\n\nDrv_Trace: No attached device: Look for any device except dummy driver.\n\n"));
|
|
GraphicsState = GraphicsStateNoAttach;
|
|
continue;
|
|
}
|
|
|
|
if (!displayInstalled &&
|
|
(GraphicsState == GraphicsStateNoAttach) &&
|
|
(gProtocolType == PROTOCOL_CONSOLE ))
|
|
{
|
|
TRACE_INIT(("\n\nDrv_Trace: No attached device: Look for any device including dummy driver.\n\n"));
|
|
GraphicsState = GraphicsStateAttachDisconnect;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If the display drivers have been installed, then look for the
|
|
// Mirroring devices - as long as we are not in basevideo !
|
|
//
|
|
|
|
if (displayInstalled &&
|
|
(bAttachMirroring == FALSE))
|
|
{
|
|
TRACE_INIT(("\n\nDrv_Trace: DrvCreateMDEV: Look for Mirroring drivers\n\n"));
|
|
bAttachMirroring = TRUE;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// We must be done. So if we did install the display driver, just
|
|
// break out of this.
|
|
//
|
|
|
|
if (displayInstalled)
|
|
{
|
|
retStatus = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// There are no devices we can work with in the registry.
|
|
// We have a real failure and take appropriate action.
|
|
//
|
|
|
|
//
|
|
// If we failed on the first driver, then we can assume their is no
|
|
// driver installed.
|
|
//
|
|
|
|
if (devNum == 0)
|
|
{
|
|
WARNING("No kernel drivers initialized");
|
|
retStatus = STATUS_DEVICE_DOES_NOT_EXIST;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If the display driver is not installed, then this is another
|
|
// bad failure - report it.
|
|
//
|
|
|
|
if (!displayInstalled)
|
|
{
|
|
//This RIP is hit often in stress.
|
|
//RIP("Drv_Trace: DrvCreateMDEV: No display drivers loaded");
|
|
retStatus = STATUS_DRIVER_UNABLE_TO_LOAD;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Never get here !
|
|
//
|
|
|
|
retStatus = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
|
|
/*****************************************************************
|
|
*****************************************************************
|
|
Check Flags and calculate rectangles
|
|
*****************************************************************
|
|
*****************************************************************/
|
|
|
|
TRACE_INIT(("\nDrv_Trace: DrvCreateMDEV: Check flags and rectangles\n"));
|
|
|
|
if (retStatus == STATUS_SUCCESS)
|
|
{
|
|
if (ulFlags & GRE_DISP_NOT_APARTOF_DESKTOP)
|
|
{
|
|
//
|
|
// This MDEV is created for additional secondary use.
|
|
// Don't change any attribute in PDEV based on this MDEV,
|
|
// since these PDEV could be a part of other MDEV which
|
|
// ,for example, represents current desktop.
|
|
//
|
|
}
|
|
else
|
|
{
|
|
ULONG i;
|
|
LPRECT pSrcRect = NULL;
|
|
LPRECT pDstRect = NULL;
|
|
PGRAPHICS_DEVICE PhysDisp;
|
|
ULONG primary = 0;
|
|
PGRAPHICS_DEVICE primaryPhysDisp = NULL;
|
|
ULONG size;
|
|
|
|
//
|
|
// Make sure there is only one primary.
|
|
//
|
|
|
|
for (i = 0; i < pmdev->chdev; i++)
|
|
{
|
|
PDEVOBJ pdo(pmdev->Dev[i].hdev);
|
|
|
|
PhysDisp = pdo.ppdev->pGraphicsDevice;
|
|
|
|
//
|
|
// The first non-removable PhysDisp would be a primary candicate
|
|
//
|
|
if (PhysDisp->stateFlags & (DISPLAY_DEVICE_REMOVABLE | DISPLAY_DEVICE_MIRRORING_DRIVER))
|
|
{
|
|
if (PhysDisp->stateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
|
|
{
|
|
TRACE_INIT(("Drv_Trace: DrvCreateMDEV: The primary desktop is on a removable or Mirror device.\n"));
|
|
|
|
PhysDisp->stateFlags &= ~DISPLAY_DEVICE_PRIMARY_DEVICE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (primaryPhysDisp == NULL) {
|
|
primary = i;
|
|
}
|
|
}
|
|
|
|
if (PhysDisp->stateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
|
|
{
|
|
if (primaryPhysDisp == NULL)
|
|
{
|
|
primaryPhysDisp = PhysDisp;
|
|
primary = i;
|
|
}
|
|
else
|
|
{
|
|
ASSERTGDI(FALSE, "Two primary devices\n");
|
|
|
|
PhysDisp->stateFlags &=
|
|
~DISPLAY_DEVICE_PRIMARY_DEVICE;
|
|
|
|
retStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create a list of rects which we can align.
|
|
//
|
|
|
|
size = pmdev->chdev * sizeof(RECT);
|
|
|
|
pSrcRect = (LPRECT) PALLOCNOZ(size, GDITAG_DRVSUP);
|
|
pDstRect = (LPRECT) PALLOCNOZ(size, GDITAG_DRVSUP);
|
|
|
|
if (pSrcRect && pDstRect)
|
|
{
|
|
ULONG ci = 0;
|
|
|
|
for (i = 0; i < pmdev->chdev; i++)
|
|
{
|
|
PDEVOBJ pdo(pmdev->Dev[i].hdev);
|
|
PDEVMODEW pdm = pdo.ppdev->ppdevDevmode;
|
|
|
|
//
|
|
// Reset the position rect with the real values.
|
|
//
|
|
|
|
(pSrcRect + i)->left = pdm->dmPosition.x;
|
|
(pSrcRect + i)->top = pdm->dmPosition.y;
|
|
(pSrcRect + i)->right = pdm->dmPosition.x + pdm->dmPelsWidth;
|
|
(pSrcRect + i)->bottom = pdm->dmPosition.y + pdm->dmPelsHeight;
|
|
|
|
if (pdo.ppdev->pGraphicsDevice->stateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER)
|
|
{
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Find a primary if we don't already have one.
|
|
// The first device with 0,0 as the origin will be it.
|
|
//
|
|
|
|
if ( (primaryPhysDisp == NULL) &&
|
|
(pdm->dmPosition.x == 0) &&
|
|
(pdm->dmPosition.y == 0) &&
|
|
((pdo.ppdev->pGraphicsDevice->stateFlags & DISPLAY_DEVICE_REMOVABLE) == 0))
|
|
{
|
|
primary = i;
|
|
primaryPhysDisp = pdo.ppdev->pGraphicsDevice;
|
|
}
|
|
|
|
ci++;
|
|
}
|
|
}
|
|
|
|
RtlCopyMemory(pDstRect, pSrcRect, size);
|
|
|
|
//
|
|
// Set the primary
|
|
//
|
|
|
|
PDEVOBJ pdo(pmdev->Dev[primary].hdev);
|
|
pdo.ppdev->pGraphicsDevice->stateFlags |= DISPLAY_DEVICE_PRIMARY_DEVICE;
|
|
|
|
|
|
|
|
//
|
|
// NOTE
|
|
// CUDR_NOSNAPTOGRID == 1 in winuser.w
|
|
//
|
|
|
|
if (AlignRects(pDstRect, ci, primary, 1) == FALSE)
|
|
{
|
|
//
|
|
// Devices could not be aligned.
|
|
//
|
|
}
|
|
|
|
if (!RtlEqualMemory(pDstRect, pSrcRect, ci * sizeof(RECT)))
|
|
{
|
|
//
|
|
// Devices were repositioned
|
|
//
|
|
|
|
WARNING("GDI DDML: Device positions are adjusted");
|
|
}
|
|
|
|
//
|
|
// Let's save all these rectangles
|
|
//
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvCreateMDEV: Reseting device positions\n"));
|
|
|
|
for (i = 0; i < pmdev->chdev; i++)
|
|
{
|
|
PDEVOBJ pdo(pmdev->Dev[i].hdev);
|
|
|
|
TRACE_INIT(("\t%ws: ", pdo.ppdev->pGraphicsDevice->szNtDeviceName));
|
|
|
|
//
|
|
// Set the surface's origin
|
|
//
|
|
|
|
pdo.ppdev->ptlOrigin = *((PPOINTL) (pDstRect + i));
|
|
|
|
//
|
|
// Notify the surface's origin to driver.
|
|
//
|
|
|
|
if (PPFNDRV(pdo,Notify))
|
|
{
|
|
PPFNDRV(pdo,Notify)(pdo.pSurface()->pSurfobj(),
|
|
DN_DEVICE_ORIGIN,
|
|
(PVOID)(&(pdo.ppdev->ptlOrigin)));
|
|
}
|
|
|
|
//
|
|
// Save the rectangle back into the mdev structure.
|
|
//
|
|
|
|
pmdev->Dev[i].rect = *(pDstRect + i);
|
|
|
|
#if DBG_BASIC
|
|
DbgPrint("GDI DDML: Device %d, position %d, %d, %d, %d, rotation %lu\n",
|
|
i,
|
|
pdo.pptlOrigin()->x,
|
|
pdo.pptlOrigin()->y,
|
|
pdo.pptlOrigin()->x +
|
|
pdo.ppdev->ppdevDevmode->dmPelsWidth,
|
|
pdo.pptlOrigin()->y +
|
|
pdo.ppdev->ppdevDevmode->dmPelsHeight,
|
|
pdo.ppdev->ppdevDevmode->dmDisplayOrientation * 90
|
|
);
|
|
|
|
if (pmdev->Dev[i].rect.left != pdo.pptlOrigin()->x)
|
|
{
|
|
DbgPrint("GDI DDML: Inconsistent rect left (%d) - origin left (%d)\n",
|
|
pmdev->Dev[i].rect.left,pdo.pptlOrigin()->x);
|
|
}
|
|
|
|
if (pmdev->Dev[i].rect.top != pdo.pptlOrigin()->y)
|
|
{
|
|
DbgPrint("GDI DDML: Inconsistent rect top (%d) - origin top (%d)\n",
|
|
pmdev->Dev[i].rect.top,pdo.pptlOrigin()->y);
|
|
}
|
|
|
|
if ((ULONG) pmdev->Dev[i].rect.right !=
|
|
pdo.pptlOrigin()->x + pdo.ppdev->ppdevDevmode->dmPelsWidth)
|
|
{
|
|
DbgPrint("GDI DDML: Inconsistent rect right (%d) - devmode right (%d)\n",
|
|
pmdev->Dev[i].rect.right,
|
|
pdo.pptlOrigin()->x + pdo.ppdev->ppdevDevmode->dmPelsWidth);
|
|
}
|
|
|
|
if ((ULONG) pmdev->Dev[i].rect.bottom !=
|
|
pdo.pptlOrigin()->y + pdo.ppdev->ppdevDevmode->dmPelsHeight)
|
|
{
|
|
DbgPrint("GDI DDML: Inconsistent rect bottom (%d) - devmode bottom (%d)\n",
|
|
pmdev->Dev[i].rect.bottom,
|
|
pdo.pptlOrigin()->y + pdo.ppdev->ppdevDevmode->dmPelsHeight);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (pSrcRect)
|
|
{
|
|
VFREEMEM(pSrcRect);
|
|
}
|
|
|
|
if (pDstRect)
|
|
{
|
|
VFREEMEM(pDstRect);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(retStatus))
|
|
{
|
|
//
|
|
// Delete all the hdevs we have created. and restore the old
|
|
// ones.
|
|
//
|
|
|
|
DrvBackoutMDEV(pmdev);
|
|
VFREEMEM(pmdev);
|
|
pmdev = NULL;
|
|
}
|
|
|
|
#if TEXTURE_DEMO
|
|
pmdev = pmdevSetupTextureDemo(pmdev);
|
|
#endif
|
|
|
|
//
|
|
// Dump the MDEV structure.
|
|
//
|
|
|
|
TRACE_INIT(("DrvCreateMDEV: Resulting MDEV\n"));
|
|
TRACE_INIT(("pmdev = %08lx\n", pmdev));
|
|
|
|
if (pmdev)
|
|
{
|
|
ULONG i;
|
|
|
|
for (i = 0; i < pmdev->chdev; i++)
|
|
{
|
|
TRACE_INIT(("\t[%d].hdev = %08lx\n", i, pmdev->Dev[i].hdev));
|
|
TRACE_INIT(("\t[%d].rect = %d, %d, %d, %d,\n", i,
|
|
pmdev->Dev[i].rect.left, pmdev->Dev[i].rect.top,
|
|
pmdev->Dev[i].rect.right, pmdev->Dev[i].rect.bottom));
|
|
}
|
|
}
|
|
|
|
return (pmdev);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrvSetBaseVideo
|
|
*
|
|
* Initialize the graphics components of the system
|
|
*
|
|
* andreva Created
|
|
\***************************************************************************/
|
|
|
|
VOID
|
|
DrvSetBaseVideo(
|
|
BOOL bSet
|
|
)
|
|
{
|
|
gbBaseVideo = bSet;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrvCheckUpgradeSettings
|
|
*
|
|
* Check first boot from upgrade. If so, get last settings and move it
|
|
* into normal setting registry. Otherwise, check if have registry settings
|
|
* already, if not, move preferred mode into normal setting registry.
|
|
*
|
|
* dennyd Created
|
|
\***************************************************************************/
|
|
|
|
DEFINE_GUID(GUID_DISPLAY_ADAPTER_INTERFACE, 0x5b45201d, 0xf2f2, 0x4f3b, 0x85, 0xbb, 0x30, 0xff, 0x1f, 0x95, 0x35, 0x99);
|
|
|
|
VOID
|
|
DrvCheckUpgradeSettings(VOID)
|
|
{
|
|
PGRAPHICS_DEVICE PhysDisp;
|
|
PWSTR SymbolicLinkList;
|
|
UNICODE_STRING SymbolicLinkName;
|
|
HANDLE hkRegistry = NULL;
|
|
NTSTATUS Status;
|
|
USHORT DualviewIndex;
|
|
DEVMODEW devMode;
|
|
BOOL bUpgraded;
|
|
UNICODE_STRING SubKeyName;
|
|
HANDLE hkSubKey = NULL;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
DWORD UsePreferredMode;
|
|
|
|
for (PhysDisp = gpGraphicsDeviceList;
|
|
PhysDisp != NULL;
|
|
PhysDisp = PhysDisp->pNextGraphicsDevice)
|
|
{
|
|
GUID Guid = GUID_DISPLAY_ADAPTER_INTERFACE;
|
|
bUpgraded = FALSE;
|
|
UsePreferredMode = 0;
|
|
DualviewIndex = 0;
|
|
|
|
if (PhysDisp->pPhysDeviceHandle == NULL)
|
|
continue;
|
|
|
|
//
|
|
// Check if it's DualView second, assign an index to it
|
|
//
|
|
if (PhysDisp->stateFlags & DISPLAY_DEVICE_DUALVIEW)
|
|
{
|
|
PGRAPHICS_DEVICE PhysDisp1;
|
|
for (PhysDisp1 = gpGraphicsDeviceList;
|
|
PhysDisp1 != PhysDisp;
|
|
PhysDisp1 = PhysDisp1->pNextGraphicsDevice)
|
|
{
|
|
if ((PhysDisp->stateFlags & DISPLAY_DEVICE_DUALVIEW) &&
|
|
(PhysDisp->pPhysDeviceHandle == PhysDisp1->pPhysDeviceHandle))
|
|
{
|
|
DualviewIndex++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(IoGetDeviceInterfaces(&Guid,
|
|
(PDEVICE_OBJECT)PhysDisp->pPhysDeviceHandle,
|
|
0,
|
|
&SymbolicLinkList)) )
|
|
{
|
|
if (SymbolicLinkList[0] != L'\0')
|
|
{
|
|
WCHAR wstrSubKey[4];
|
|
|
|
RtlInitUnicodeString(&SymbolicLinkName, SymbolicLinkList);
|
|
|
|
if (NT_SUCCESS(IoOpenDeviceInterfaceRegistryKey(&SymbolicLinkName,
|
|
KEY_ALL_ACCESS,
|
|
&hkRegistry)))
|
|
{
|
|
ULONG defaultValue = 0, Attached;
|
|
|
|
//
|
|
// Read and delete the display settings
|
|
//
|
|
|
|
RtlZeroMemory(&devMode, sizeof(devMode));
|
|
swprintf(wstrSubKey, L"%d", DualviewIndex);
|
|
|
|
RTL_QUERY_REGISTRY_TABLE QueryTable[] =
|
|
{
|
|
{NULL, RTL_QUERY_REGISTRY_SUBKEY,
|
|
wstrSubKey, NULL, REG_NONE, NULL, 0},
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT|RTL_QUERY_REGISTRY_DELETE, L"UsePreferredMode",
|
|
&UsePreferredMode, REG_DWORD, &defaultValue, 4},
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT|RTL_QUERY_REGISTRY_DELETE, DefaultSettings[0],
|
|
&devMode.dmBitsPerPel, REG_DWORD, &defaultValue, 4},
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT|RTL_QUERY_REGISTRY_DELETE, DefaultSettings[1],
|
|
&devMode.dmPelsWidth, REG_DWORD, &defaultValue, 4},
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT|RTL_QUERY_REGISTRY_DELETE, DefaultSettings[2],
|
|
&devMode.dmPelsHeight, REG_DWORD, &defaultValue, 4},
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT|RTL_QUERY_REGISTRY_DELETE, DefaultSettings[3],
|
|
&devMode.dmDisplayFrequency, REG_DWORD, &defaultValue, 4},
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT|RTL_QUERY_REGISTRY_DELETE, DefaultSettings[4],
|
|
&devMode.dmDisplayFlags, REG_DWORD, &defaultValue, 4},
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT|RTL_QUERY_REGISTRY_DELETE, DefaultSettings[7],
|
|
&devMode.dmDisplayOrientation, REG_DWORD, &defaultValue, 4},
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT|RTL_QUERY_REGISTRY_DELETE, DefaultSettings[8],
|
|
&devMode.dmDisplayFixedOutput, REG_DWORD, &defaultValue, 4},
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT|RTL_QUERY_REGISTRY_DELETE, DefaultSettings[9],
|
|
&devMode.dmPosition.x, REG_DWORD, &defaultValue, 4},
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT|RTL_QUERY_REGISTRY_DELETE, DefaultSettings[10],
|
|
&devMode.dmPosition.y, REG_DWORD, &defaultValue, 4},
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT|RTL_QUERY_REGISTRY_DELETE, DefaultSettings[11],
|
|
&Attached, REG_DWORD, &defaultValue, 4},
|
|
{NULL, 0, NULL}
|
|
};
|
|
|
|
if (NT_SUCCESS(RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
|
|
(PWSTR)hkRegistry,
|
|
&QueryTable[0],
|
|
NULL,
|
|
NULL)) &&
|
|
(devMode.dmPelsWidth != 0) &&
|
|
(!UsePreferredMode)
|
|
)
|
|
{
|
|
if (Attached) {
|
|
devMode.dmFields |= DM_POSITION;
|
|
}
|
|
|
|
DrvUpdateDisplayDriverParameters(PhysDisp,
|
|
&devMode,
|
|
(Attached == 0),
|
|
TRUE);
|
|
bUpgraded = TRUE;
|
|
}
|
|
|
|
//
|
|
// Delete the subkey containing the display settings
|
|
//
|
|
|
|
RtlInitUnicodeString(&SubKeyName, wstrSubKey);
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&SubKeyName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
hkRegistry,
|
|
NULL);
|
|
|
|
if (NT_SUCCESS(ZwOpenKey(&hkSubKey,
|
|
KEY_ALL_ACCESS,
|
|
&ObjectAttributes))
|
|
)
|
|
{
|
|
ZwDeleteKey(hkSubKey);
|
|
}
|
|
|
|
//
|
|
// Close the device interface registry key
|
|
//
|
|
|
|
ZwClose(hkRegistry);
|
|
}
|
|
}
|
|
ExFreePool((PVOID)SymbolicLinkList);
|
|
}
|
|
|
|
if (PhysDisp->stateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check if there are still registry settings.
|
|
// If not, put preferred mode there.
|
|
//
|
|
|
|
PDEVMODEW pDevmode = (PDEVMODEW) PALLOCNOZ(sizeof(DEVMODEW) + MAXUSHORT,
|
|
GDITAG_DEVMODE);
|
|
if (pDevmode == NULL) {
|
|
continue;
|
|
}
|
|
|
|
RtlZeroMemory(pDevmode, sizeof(DEVMODEW));
|
|
pDevmode->dmSize = 0xDDDD;
|
|
pDevmode->dmDriverExtra = MAXUSHORT;
|
|
|
|
if (UsePreferredMode ||
|
|
(!bUpgraded &&
|
|
(!NT_SUCCESS(DrvGetDisplayDriverParameters(PhysDisp,
|
|
pDevmode,
|
|
FALSE,
|
|
FALSE)) ||
|
|
(pDevmode->dmPelsWidth == 0)
|
|
)
|
|
)
|
|
)
|
|
{
|
|
RtlZeroMemory(pDevmode, sizeof(DEVMODEW));
|
|
pDevmode->dmSize = sizeof(DEVMODEW);
|
|
|
|
if (NT_SUCCESS(DrvGetPreferredMode(pDevmode, PhysDisp)))
|
|
{
|
|
pDevmode->dmBitsPerPel = 32;
|
|
pDevmode->dmFields |= DM_BITSPERPEL;
|
|
DrvUpdateDisplayDriverParameters(PhysDisp,
|
|
pDevmode,
|
|
FALSE,
|
|
TRUE);
|
|
}
|
|
}
|
|
|
|
VFREEMEM(pDevmode);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrvInitConsole
|
|
*
|
|
* Initialize the graphics components of the system
|
|
*
|
|
* andreva Created
|
|
\***************************************************************************/
|
|
|
|
VOID
|
|
DrvInitConsole(
|
|
BOOL bEnumerationNeeded)
|
|
{
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE hkRegistry = NULL;
|
|
NTSTATUS Status;
|
|
PGRAPHICS_DEVICE PhysDisp;
|
|
PVOID RegistrationHandle;
|
|
|
|
/*****************************************************************
|
|
*****************************************************************
|
|
BaseVideo
|
|
*****************************************************************
|
|
*****************************************************************/
|
|
|
|
//
|
|
// Basevideo is considered a primary device in that the user will run
|
|
// the vga driver. This does override any other primary selection
|
|
// the user may have put in the registry.
|
|
//
|
|
|
|
RtlInitUnicodeString(&UnicodeString,
|
|
L"\\Registry\\Machine\\System\\CurrentControlSet\\"
|
|
L"Control\\GraphicsDrivers\\BaseVideo");
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&UnicodeString,
|
|
OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = ZwOpenKey(&hkRegistry,
|
|
KEY_READ,
|
|
&ObjectAttributes);
|
|
#ifdef _HYDRA_
|
|
/*
|
|
* No base video for WinStations
|
|
*/
|
|
if ( !G_fConsole )
|
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
#endif
|
|
|
|
|
|
if (NT_SUCCESS( Status))
|
|
{
|
|
TRACE_INIT(("Drv_Trace: DrvInitConsole: Basevideo - FOUND\n"));
|
|
|
|
DrvSetBaseVideo(TRUE);
|
|
|
|
if (hkRegistry)
|
|
ZwCloseKey(hkRegistry);
|
|
}
|
|
else
|
|
{
|
|
TRACE_INIT(("Drv_Trace: DrvInitConsole: Basevideo - NOT FOUND\n"));
|
|
|
|
DrvSetBaseVideo(FALSE);
|
|
}
|
|
|
|
/*****************************************************************
|
|
*****************************************************************
|
|
Device List
|
|
*****************************************************************
|
|
*****************************************************************/
|
|
|
|
|
|
RtlZeroMemory(&gFullscreenGraphicsDevice, sizeof(GRAPHICS_DEVICE));
|
|
RtlZeroMemory(&gFeFullscreenGraphicsDevice, sizeof(GRAPHICS_DEVICE));
|
|
|
|
//
|
|
// Register for new device notifucations
|
|
//
|
|
#if 0
|
|
TRACE_INIT(("Drv_Trace: DrvInitConsole: Registering GUIDs\n"));
|
|
|
|
_asm {int 3};
|
|
|
|
Status = IoRegisterPlugPlayNotification (
|
|
EventCategoryDeviceInterfaceChange,
|
|
PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
|
|
(LPGUID) &GUID_DISPLAY_DEVICE_INTERFACE_STANDARD,
|
|
gpWin32kDriverObject,
|
|
(PDRIVER_NOTIFICATION_CALLBACK_ROUTINE)DrvNewDisplayDevice,
|
|
NULL,
|
|
&RegistrationHandle);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ASSERTGDI(FALSE, "IoRegisterPlugPlayNotification(display) failed");
|
|
return;
|
|
}
|
|
|
|
Status = IoRegisterPlugPlayNotification (
|
|
EventCategoryDeviceInterfaceChange,
|
|
PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
|
|
(LPGUID) &GUID_DISPLAY_OUTPUT_INTERFACE_STANDARD,
|
|
gpWin32kDriverObject,
|
|
(PDRIVER_NOTIFICATION_CALLBACK_ROUTINE)DrvNewDisplayOutput,
|
|
NULL,
|
|
&RegistrationHandle);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ASSERTGDI(FALSE, "IoRegisterPlugPlayNotification(output) failed");
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
DrvUpdateGraphicsDeviceList(TRUE, bEnumerationNeeded, gProtocolType == PROTOCOL_CONSOLE);
|
|
|
|
DrvCheckUpgradeSettings();
|
|
|
|
#if 0
|
|
DrvGetMirrorDrivers();
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrvSetSharedDevLock
|
|
*
|
|
* routine to share devlock between parent and children.
|
|
*
|
|
* 05-Mar-1998 hideyukn Created
|
|
\***************************************************************************/
|
|
|
|
VOID
|
|
DrvSetSharedDevLock(PMDEV pmdev)
|
|
{
|
|
PDEVOBJ pdoParent(pmdev->hdevParent);
|
|
|
|
// Parent's DEVLOCK should not be shared.
|
|
|
|
ASSERTGDI(!pdoParent.bUseParentDevLock(),
|
|
"DrvSetSharedDevLock():Parent PDEV has shared devlock\n");
|
|
|
|
for (ULONG i = 0; i < pmdev->chdev; i++)
|
|
{
|
|
PDEVOBJ pdo(pmdev->Dev[i].hdev);
|
|
|
|
// Set Parent PDEV as parent to each of the PDEVs that we'll manage.
|
|
|
|
pdo.ppdev->ppdevParent = (PDEV *) pmdev->hdevParent;
|
|
|
|
// Switch to use the shared DEVLOCK with parent.
|
|
|
|
pdo.vUseParentDevLock();
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrvRealizeHalftonePalette
|
|
*
|
|
* routine to realize halftone palette onto given device.
|
|
*
|
|
* 22-Apr-1998 hideyukn Created
|
|
\***************************************************************************/
|
|
|
|
PPALETTE gppalHalftone = NULL;
|
|
ULONG gulPalTimeForDDML = 0;
|
|
|
|
PPALETTE
|
|
DrvRealizeHalftonePalette(HDEV hdevPalette, BOOL bForce)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
PDEVOBJ pdoPalette(hdevPalette);
|
|
XEPALOBJ palSurfObj(pdoPalette.ppalSurf());
|
|
|
|
if (bForce || (gulPalTimeForDDML != palSurfObj.ulTime()))
|
|
{
|
|
//
|
|
// Create display DC for palette device.
|
|
//
|
|
HDC hdcDevice = GreCreateDisplayDC(hdevPalette,DCTYPE_DIRECT,FALSE);
|
|
|
|
if (hdcDevice)
|
|
{
|
|
if (gppalHalftone == NULL)
|
|
{
|
|
//
|
|
// Create Win98 compatible halftone palette.
|
|
//
|
|
HPALETTE hPalette = GreCreateCompatibleHalftonePalette(hdcDevice);
|
|
|
|
if (hPalette)
|
|
{
|
|
EPALOBJ palObj(hPalette);
|
|
|
|
if (GreSetPaletteOwner(hPalette, OBJECT_OWNER_PUBLIC))
|
|
{
|
|
//
|
|
// Put it into Global variable.
|
|
//
|
|
gppalHalftone = palObj.ppalGet();
|
|
}
|
|
else
|
|
{
|
|
bDeletePalette((HPAL)hPalette,TRUE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WARNING("DrvRealizeHalftonePalette():Failed on GreCreateCompatibleHalftonePalette()");
|
|
}
|
|
}
|
|
|
|
if (gppalHalftone)
|
|
{
|
|
//
|
|
// Selet the global halftone palette into the palette device.
|
|
//
|
|
HPALETTE hPalOld = GreSelectPalette(hdcDevice,
|
|
(HPALETTE)gppalHalftone->hGet(),
|
|
TRUE); // ForceBackgound
|
|
|
|
if (hPalOld)
|
|
{
|
|
XEPALOBJ palHTObj(gppalHalftone);
|
|
|
|
//
|
|
// Strip translation table.
|
|
//
|
|
palHTObj.vMakeNoXlate();
|
|
|
|
//
|
|
// Realize halftone palette on device. (device palette might be changed)
|
|
//
|
|
if (GreRealizePalette(hdcDevice))
|
|
{
|
|
KdPrint(("DrvRealizeHalftonePalette():Device palette has been changed\n"));
|
|
}
|
|
|
|
//
|
|
// Update palette time.
|
|
//
|
|
|
|
gulPalTimeForDDML = palSurfObj.ulTime();
|
|
|
|
//
|
|
// Select back old one.
|
|
//
|
|
GreSelectPalette(hdcDevice,hPalOld,FALSE);
|
|
|
|
bRet = TRUE;
|
|
}
|
|
else
|
|
{
|
|
WARNING("DrvRealizeHalftonePalette():Failed on GreSelectPalette()");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WARNING("DrvRealizeHalftonePalette():gppalHalftone is NULL");
|
|
}
|
|
|
|
bDeleteDCInternal(hdcDevice,TRUE,FALSE);
|
|
}
|
|
else
|
|
{
|
|
WARNING("DrvRealizeHalftonePalette():Failed on GreCreateDisplayDC()");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Realization is still effective.
|
|
//
|
|
|
|
bRet = TRUE;
|
|
}
|
|
|
|
//
|
|
// If we could not have halftone palette, just use default palette.
|
|
//
|
|
|
|
return (bRet ? gppalHalftone : ppalDefault);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrvSetSharedPalette
|
|
*
|
|
* routine to share palette between parent and children.
|
|
*
|
|
* 22-Apr-1998 hideyukn Created
|
|
\***************************************************************************/
|
|
|
|
BOOL MulSetPalette(DHPDEV,PALOBJ *,FLONG,ULONG,ULONG);
|
|
|
|
HDEV
|
|
DrvSetSharedPalette(PMDEV pmdev)
|
|
{
|
|
HDEV hdevPalette = NULL;
|
|
|
|
//
|
|
// ToddLa's law - Palette should be shared with all paletaized monitors.
|
|
//
|
|
|
|
PDEVOBJ pdoParent(pmdev->hdevParent);
|
|
|
|
//ASSERTGDI((PPFNDRV(pdoParent,SetPalette)) == MulSetPalette,
|
|
// "DrvSetSharedPalette(): SetPalette != MulSetPalette\n");
|
|
|
|
PPALETTE ppalShared = NULL;
|
|
|
|
if (pdoParent.bIsPalManaged())
|
|
{
|
|
//
|
|
// If parent is palette device, we shared it
|
|
// with all paletaized children.
|
|
//
|
|
|
|
ppalShared = pdoParent.ppalSurf();
|
|
|
|
//
|
|
// Remember the hdev which owns palette.
|
|
//
|
|
|
|
hdevPalette = pdoParent.hdev();
|
|
}
|
|
|
|
for (UINT i = 0; i < pmdev->chdev; i++)
|
|
{
|
|
PDEVOBJ pdoChild(pmdev->Dev[i].hdev);
|
|
|
|
if (pdoChild.bIsPalManaged())
|
|
{
|
|
//
|
|
// Change pointer to DrvSetPalette to
|
|
// DDML, so that palette can be changed
|
|
// to a specific hdev, will be dispatch
|
|
// to every paletaized device.
|
|
//
|
|
|
|
pdoChild.pfnSetPalette(MulSetPalette);
|
|
|
|
if (ppalShared == NULL)
|
|
{
|
|
//
|
|
// Parent is not palette device, but
|
|
// this is palette device which we
|
|
// encounter first in the children,
|
|
// so we will share this palette.
|
|
//
|
|
|
|
ppalShared = pdoChild.ppalSurf();
|
|
|
|
//
|
|
// Remember the hdev which owns palette.
|
|
//
|
|
|
|
hdevPalette = pdoChild.hdev();
|
|
}
|
|
else if (pdoChild.ppalSurf() != ppalShared)
|
|
{
|
|
//
|
|
// If the palette in hdev is already same as
|
|
// parent (mostly it is primary device, if
|
|
// primary is palette device.), don't
|
|
// need to update it. Otherwise update it here.
|
|
//
|
|
|
|
XEPALOBJ palChild(pdoChild.ppalSurf());
|
|
|
|
//
|
|
// Set colour table to shared palette.
|
|
//
|
|
|
|
palChild.apalColorSet(ppalShared);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (hdevPalette);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrvTransferGdiObjects()
|
|
*
|
|
* Transfer belonging gdi object from a hdev to other.
|
|
*
|
|
* 04-Jul-1998 hideyukn Created
|
|
\***************************************************************************/
|
|
|
|
#define DRV_TRANS_DC_TYPE 0x0001
|
|
#define DRV_TRANS_SURF_TYPE 0x0002
|
|
#define DRV_TRANS_DRVOBJ_TYPE 0x0004
|
|
#define DRV_TRANS_WNDOBJ_TYPE 0x0008
|
|
#define DRV_TRANS_ALL_TYPE 0x000F
|
|
#define DRV_TRANS_TO_CLONE 0x1000
|
|
|
|
VOID
|
|
DrvTransferGdiObjects(HDEV hdevNew, HDEV hdevOld, ULONG ulFlags)
|
|
{
|
|
//
|
|
// 1) Change owner of DC_TYPE object.
|
|
//
|
|
// 2) Change owner of SURF_TYPE object.
|
|
//
|
|
// + ATI driver, for example, creates engine
|
|
// bitmap for thier banking, off-screen
|
|
// bitmap and ..., so need to change owner
|
|
// of those bitmap to clone's hdev.
|
|
//
|
|
// 3) Change owner of DRVOBJ_TYPE object.
|
|
//
|
|
// 4) Exchange WNDOBJ.
|
|
//
|
|
|
|
PDEVOBJ pdoNew(hdevNew);
|
|
PDEVOBJ pdoOld(hdevOld);
|
|
|
|
ASSERTGDI(pdoNew.pSurface() == pdoOld.pSurface(),
|
|
"DrvTransferGdiObjects():pSurface does not match\n");
|
|
|
|
ASSERTGDI(pdoNew.dhpdev() == pdoOld.dhpdev(),
|
|
"DrvTransferGdiObjects():dhpdev does not match\n");
|
|
|
|
GreAcquireHmgrSemaphore();
|
|
|
|
HOBJ hobj = 0;
|
|
|
|
//
|
|
// Transfer DC which own by hdevCloned to hdevClone.
|
|
//
|
|
|
|
if (ulFlags & DRV_TRANS_DC_TYPE)
|
|
{
|
|
PDC pdc = NULL;
|
|
hobj = 0;
|
|
|
|
while (pdc = (DC*) HmgSafeNextObjt(hobj, DC_TYPE))
|
|
{
|
|
hobj = (HOBJ) pdc->hGet();
|
|
|
|
if ((HDEV)pdc->ppdev() == hdevOld)
|
|
{
|
|
KdPrint(("Transfer DC %x - hdevNew %x hdevOld %x \n",
|
|
pdc->hGet(),hdevNew,hdevOld));
|
|
|
|
pdc->ppdev((PDEV *)pdoNew.hdev());
|
|
|
|
if (ulFlags & DRV_TRANS_TO_CLONE)
|
|
{
|
|
pdc->fsSet(DC_IN_CLONEPDEV);
|
|
}
|
|
else
|
|
{
|
|
pdc->fsClr(DC_IN_CLONEPDEV);
|
|
}
|
|
|
|
pdoNew.vReferencePdev();
|
|
pdoOld.vUnreferencePdev();
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Transfer surface which own by old pdev to new pdev.
|
|
//
|
|
|
|
if (ulFlags & DRV_TRANS_SURF_TYPE)
|
|
{
|
|
SURFACE *pSurface = NULL;
|
|
hobj = 0;
|
|
|
|
while (pSurface = (SURFACE*) HmgSafeNextObjt(hobj, SURF_TYPE))
|
|
{
|
|
hobj = (HOBJ) pSurface->hGet();
|
|
|
|
if ((pSurface->hdev() == hdevOld) /* && pSurface->bDriverCreated() */)
|
|
{
|
|
KdPrint(("Transfer surface %x - hdevNew %x hdevOld %x \n",
|
|
pSurface->hGet(),hdevNew,hdevOld));
|
|
|
|
pSurface->hdev(hdevNew);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Transfer DRVOBJ.
|
|
//
|
|
|
|
if (ulFlags & DRV_TRANS_DRVOBJ_TYPE)
|
|
{
|
|
DRVOBJ *pdrvo = NULL;
|
|
hobj = 0;
|
|
|
|
while (pdrvo = (DRVOBJ*) HmgSafeNextObjt(hobj, DRVOBJ_TYPE))
|
|
{
|
|
hobj = (HOBJ) pdrvo->hGet();
|
|
|
|
if (pdrvo->hdev == hdevOld)
|
|
{
|
|
KdPrint(("Transfer drvobj %x - hdevNew %x hdevOld %x \n",
|
|
pdrvo->hGet(),hdevNew,hdevOld));
|
|
|
|
pdrvo->hdev = hdevNew;
|
|
pdoNew.vReferencePdev();
|
|
pdoOld.vUnreferencePdev();
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Transfer WNDOBJ.
|
|
//
|
|
if (ulFlags & DRV_TRANS_WNDOBJ_TYPE)
|
|
{
|
|
vTransferWndObjs(pdoNew.pSurface(),pdoOld.hdev(),pdoNew.hdev());
|
|
}
|
|
|
|
GreReleaseHmgrSemaphore();
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrvEnableDirectDrawForModeChange()
|
|
*
|
|
* 01-Aug-1998 hideyukn Created
|
|
\***************************************************************************/
|
|
|
|
VOID
|
|
DrvEnableDirectDrawForModeChange(
|
|
HDEV *phdevList,
|
|
BOOL bAlloc
|
|
)
|
|
{
|
|
ASSERTGDI(GreIsSemaphoreOwnedByCurrentThread(ghsemShareDevLock),
|
|
"ShareDevlock must be held be before calling EnableDirectDraw");
|
|
|
|
ULONG chdev = (ULONG)(ULONG_PTR)(*phdevList);
|
|
HDEV *phdev = phdevList + 1;
|
|
|
|
for (ULONG i = 0; i < chdev; i++)
|
|
{
|
|
GreResumeDirectDraw(*phdev, FALSE);
|
|
phdev++;
|
|
}
|
|
|
|
if (bAlloc)
|
|
{
|
|
VFREEMEM(phdevList);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrvDisableDirectDrawForModeChange()
|
|
*
|
|
* 01-Aug-1998 hideyukn Created
|
|
\***************************************************************************/
|
|
|
|
HDEV *
|
|
DrvDisableDirectDrawForModeChange(
|
|
PMDEV pmdev1,
|
|
PMDEV pmdev2,
|
|
HDEV *phdevQuickList,
|
|
ULONG chdevQuickList
|
|
)
|
|
{
|
|
HDEV *phdevList, *phdev;
|
|
ULONG chdevList;
|
|
|
|
ASSERTGDI(GreIsSemaphoreOwnedByCurrentThread(ghsemShareDevLock),
|
|
"ShareDevlock must be held be before calling DisableDirectDraw");
|
|
|
|
chdevList = pmdev1->chdev + pmdev2->chdev + 2;
|
|
|
|
if (chdevQuickList >= chdevList)
|
|
{
|
|
phdevList = phdevQuickList;
|
|
}
|
|
else
|
|
{
|
|
phdevList = (HDEV *)PALLOCNOZ(chdevList * sizeof(HDEV), 'pmtG');
|
|
|
|
if (phdevList == NULL)
|
|
{
|
|
return (NULL);
|
|
}
|
|
}
|
|
|
|
phdev = phdevList + 1;
|
|
chdevList = 0;
|
|
|
|
if (pmdev1->hdevParent)
|
|
{
|
|
*phdev = pmdev1->hdevParent;
|
|
phdev++; chdevList++;
|
|
}
|
|
|
|
for (ULONG i = 0; i < pmdev1->chdev; i++)
|
|
{
|
|
*phdev = pmdev1->Dev[i].hdev;
|
|
phdev++; chdevList++;
|
|
}
|
|
|
|
if (pmdev2->hdevParent)
|
|
{
|
|
*phdev = pmdev2->hdevParent;
|
|
phdev++; chdevList++;
|
|
}
|
|
|
|
for (i = 0; i < pmdev2->chdev; i++)
|
|
{
|
|
*phdev = pmdev2->Dev[i].hdev;
|
|
phdev++; chdevList++;
|
|
}
|
|
|
|
*phdevList = (HDEV)ULongToPtr( chdevList );
|
|
|
|
for (i = 0; i < chdevList; i++)
|
|
{
|
|
PDEVOBJ pdo(phdevList[i+1]);
|
|
|
|
// Devlock must *not* be held.
|
|
|
|
pdo.vAssertNoDevLock();
|
|
|
|
GreSuspendDirectDrawEx(phdevList[i+1], DXG_SR_DDRAW_MODECHANGE);
|
|
}
|
|
|
|
return (phdevList);
|
|
}
|
|
|
|
//
|
|
// This function checks which Dualview Views will be attached. Then send a SWITCH_DUALVIEW
|
|
// notification to driver for setting of Video memory.
|
|
// It also checks if current DUALVIEW attachment state will be changed. The caller can decide
|
|
// if a PDEV can be reused or not.
|
|
//
|
|
// Return Value:
|
|
// DualviewNoSwitch: The DUALVIEW attachment state remains same
|
|
// DualviewSwitch: The DUALVIEW attachment state will be changed.
|
|
// DualviewFail: The DUALVIEW attachment state will be changed.
|
|
// And related PhysDisps have outstanding refcount and may get reused later
|
|
//
|
|
typedef enum _DUALVIEW_STATE {
|
|
DualviewNoSwitch = 0,
|
|
DualviewSwitch,
|
|
DualviewFail
|
|
} DUALVIEW_STATE;
|
|
|
|
DUALVIEW_STATE
|
|
CheckAndNotifyDualView(
|
|
PUNICODE_STRING pstrDeviceName,
|
|
PMDEV pMdevOrg,
|
|
MODE PreviousMode
|
|
)
|
|
{
|
|
PGRAPHICS_DEVICE PhysDisp;
|
|
ULONG bResetAll, bytesReturned;
|
|
ULONG primary, attach = 0;
|
|
ULONG numDualViews = 0, i, j;
|
|
BOOL bNoneToAttach = TRUE, bNoneAttached = TRUE;
|
|
BOOL bChangeDualviewState = FALSE, bHasOutstanding = FALSE;
|
|
DUALVIEW_STATE retVal;
|
|
|
|
typedef struct
|
|
{
|
|
PGRAPHICS_DEVICE PhysDisp;
|
|
ULONG Attached;
|
|
ULONG ToAttach;
|
|
} ATTACHPHYSDISP, *PATTACHPHYSDISP;
|
|
|
|
for (PhysDisp = gpGraphicsDeviceList;
|
|
PhysDisp != NULL;
|
|
PhysDisp = PhysDisp->pNextGraphicsDevice)
|
|
{
|
|
if (PhysDisp->stateFlags & DISPLAY_DEVICE_DUALVIEW)
|
|
{
|
|
numDualViews++;
|
|
}
|
|
}
|
|
|
|
if (numDualViews == 0)
|
|
return DualviewNoSwitch;
|
|
|
|
PATTACHPHYSDISP AttachPhysDisp = (PATTACHPHYSDISP) PALLOCMEM(sizeof(ATTACHPHYSDISP)*numDualViews, GDITAG_DRVSUP);
|
|
|
|
if (AttachPhysDisp == NULL)
|
|
return DualviewFail;
|
|
|
|
|
|
numDualViews = 0;
|
|
for (PhysDisp = gpGraphicsDeviceList;
|
|
PhysDisp != NULL;
|
|
PhysDisp = PhysDisp->pNextGraphicsDevice)
|
|
{
|
|
if (PhysDisp->stateFlags & (DISPLAY_DEVICE_MIRRORING_DRIVER | DISPLAY_DEVICE_DISCONNECT))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if(!GetPrimaryAttachFlags(PhysDisp, &primary, &attach))
|
|
{
|
|
VFREEMEM(AttachPhysDisp);
|
|
return DualviewFail;
|
|
}
|
|
|
|
if (attach)
|
|
{
|
|
bNoneToAttach = FALSE;
|
|
}
|
|
|
|
if (PhysDisp->stateFlags & DISPLAY_DEVICE_DUALVIEW)
|
|
{
|
|
AttachPhysDisp[numDualViews].PhysDisp = PhysDisp;
|
|
AttachPhysDisp[numDualViews].ToAttach = (attach) ? 1 : 0;
|
|
//
|
|
// Check if the PhysDisp is already attached in pMdevOrg
|
|
//
|
|
AttachPhysDisp[numDualViews].Attached = 0;
|
|
if (pMdevOrg)
|
|
{
|
|
for (i = 0; i < pMdevOrg->chdev; i++)
|
|
{
|
|
PDEVOBJ po(pMdevOrg->Dev[i].hdev);
|
|
if ((po.ppdev->pGraphicsDevice) == PhysDisp)
|
|
{
|
|
AttachPhysDisp[numDualViews].Attached = 1;
|
|
bNoneAttached = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
numDualViews++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// A little patch for Attach flag from Registry. If all of PhysDisp are NotAttach
|
|
// in registry, CreateMDEV will pick the first PhysDisp to Attach. It is a typical
|
|
// situation right after clean setup.
|
|
//
|
|
if (bNoneToAttach)
|
|
{
|
|
for (PhysDisp = gpGraphicsDeviceList;
|
|
PhysDisp != NULL;
|
|
PhysDisp = PhysDisp->pNextGraphicsDevice)
|
|
{
|
|
if (!(PhysDisp->stateFlags & (DISPLAY_DEVICE_MIRRORING_DRIVER | DISPLAY_DEVICE_DISCONNECT)))
|
|
{
|
|
if (PhysDisp->stateFlags & DISPLAY_DEVICE_DUALVIEW)
|
|
{
|
|
AttachPhysDisp[0].ToAttach = 1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!pstrDeviceName)
|
|
{
|
|
|
|
for (i = 0; i < numDualViews; i++)
|
|
{
|
|
if (AttachPhysDisp[i].Attached != AttachPhysDisp[i].ToAttach || pMdevOrg == NULL)
|
|
{
|
|
bChangeDualviewState = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Never allow ChangeDisplaySettings("\\.\DisplayX") to attach/detach one of Dualview
|
|
//
|
|
PhysDisp = DrvGetDeviceFromName(pstrDeviceName, PreviousMode);
|
|
if (PhysDisp)
|
|
{
|
|
for (i = 0; i < numDualViews; i++)
|
|
{
|
|
if (PhysDisp == AttachPhysDisp[i].PhysDisp)
|
|
{
|
|
if (AttachPhysDisp[i].Attached != AttachPhysDisp[i].ToAttach || pMdevOrg == NULL)
|
|
{
|
|
bChangeDualviewState = TRUE;
|
|
bHasOutstanding = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bChangeDualviewState)
|
|
{
|
|
if (bHasOutstanding)
|
|
{
|
|
retVal = DualviewFail;
|
|
//
|
|
// If we cannot change mode due to Dualview, restore the original
|
|
// attch state in case of surprise mode change later. Since CPL
|
|
// won't restore te attach flag.
|
|
//
|
|
if (pMdevOrg)
|
|
{
|
|
for (i = 0; i < numDualViews; i++) {
|
|
DrvUpdateAttachFlag(PhysDisp, AttachPhysDisp[i].Attached);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
retVal = DualviewSwitch;
|
|
//
|
|
// Send driver the Dualview state change notification
|
|
//
|
|
if (!pstrDeviceName)
|
|
{
|
|
for (i = 0; i < numDualViews; i++) {
|
|
GreDeviceIoControl(AttachPhysDisp[i].PhysDisp->pDeviceHandle,
|
|
IOCTL_VIDEO_SWITCH_DUALVIEW,
|
|
&(AttachPhysDisp[i].ToAttach),
|
|
sizeof(ULONG),
|
|
NULL,
|
|
0,
|
|
&bytesReturned);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERTGDI(FALSE, "Trying to attach/detach a view by ChangeDisplaySettings(DisplayX)\n");
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
retVal = DualviewNoSwitch;
|
|
}
|
|
|
|
//
|
|
// After sending the notification, the the driver may change mode list internally.
|
|
// Force PhysDisp to update mode list here
|
|
//
|
|
for (i = 0; i < numDualViews; i++)
|
|
{
|
|
DrvBuildDevmodeList(AttachPhysDisp[i].PhysDisp, TRUE);
|
|
}
|
|
|
|
VFREEMEM(AttachPhysDisp);
|
|
|
|
return retVal;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrvChangeDisplaySettings
|
|
*
|
|
* Routines to change the settings of a display device.
|
|
*
|
|
* andreva Created
|
|
\***************************************************************************/
|
|
|
|
BOOL MulEnableDriver(ULONG,ULONG,PDRVENABLEDATA);
|
|
BOOL PanEnableDriver(ULONG,ULONG,PDRVENABLEDATA);
|
|
|
|
LONG
|
|
DrvChangeDisplaySettings(
|
|
PUNICODE_STRING pstrDeviceName,
|
|
HDEV hdevPrimary,
|
|
LPDEVMODEW lpDevMode,
|
|
PVOID pDesktopId,
|
|
MODE PreviousMode,
|
|
BOOL bUpdateRegistry,
|
|
BOOL bSetMode,
|
|
PMDEV pOrgMdev,
|
|
PMDEV *pNewMdev,
|
|
DWORD PruneFlag,
|
|
BOOL bTryClosest
|
|
)
|
|
{
|
|
GDIFunctionID(DrvChangeDisplaySettings);
|
|
|
|
PGRAPHICS_DEVICE PhysDisp = NULL;
|
|
UNICODE_STRING DeviceName;
|
|
PDEVMODEW pdevmodeInformation = NULL;
|
|
BOOL bDetach = FALSE;
|
|
LONG status = GRE_DISP_CHANGE_SUCCESSFUL;
|
|
ULONG defaultValue = 0;
|
|
ULONG disableAll;
|
|
ULONG i, j;
|
|
PMDEV pmdev;
|
|
BOOL bPrune = (PruneFlag != GRE_RAWMODE);
|
|
#if DBG
|
|
ULONG oldTrace;
|
|
#endif
|
|
|
|
RTL_QUERY_REGISTRY_TABLE QueryTable[] =
|
|
{
|
|
{NULL, RTL_QUERY_REGISTRY_DIRECT, L"DisableAll", &disableAll,
|
|
REG_DWORD, &defaultValue, 4},
|
|
{NULL, 0, NULL}
|
|
};
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvChangeDisplaySettings - Enter\n"));
|
|
|
|
*pNewMdev = NULL;
|
|
gbInvalidateDualView = FALSE;
|
|
|
|
//
|
|
// Let's find the device on which the operation must be performed.
|
|
//
|
|
|
|
if (pstrDeviceName)
|
|
{
|
|
PhysDisp = DrvGetDeviceFromName(pstrDeviceName, PreviousMode);
|
|
|
|
if (PhysDisp == NULL)
|
|
{
|
|
TRACE_INIT(("Drv_Trace: DrvChangeDisplaySettings - Leave - Bad Device\n"));
|
|
return GRE_DISP_CHANGE_BADPARAM;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If (NULL, NULL) is passed in, then we want to change the
|
|
// default desktop back to what it was ...
|
|
// Otherwise, for compatibility, (NULL, DEVMODE) affect only the
|
|
// primary device.
|
|
//
|
|
|
|
if (lpDevMode)
|
|
{
|
|
PDEVOBJ pdo(hdevPrimary);
|
|
|
|
if (pdo.bValid())
|
|
{
|
|
PhysDisp = pdo.ppdev->pGraphicsDevice;
|
|
}
|
|
|
|
if (PhysDisp == NULL)
|
|
{
|
|
TRACE_INIT(("Drv_Trace: DrvChangeDisplaySettings - Leave - Bad Device\n"));
|
|
return GRE_DISP_CHANGE_BADPARAM;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (PhysDisp == (PGRAPHICS_DEVICE) DDML_DRIVER)
|
|
{
|
|
ASSERTGDI(FALSE, "Trying to change settings for DDML layer\n");
|
|
return GRE_DISP_CHANGE_BADPARAM;
|
|
}
|
|
|
|
if (PhysDisp != NULL)
|
|
{
|
|
|
|
|
|
if (PruneFlag == GRE_DEFAULT)
|
|
bPrune = DrvGetPruneFlag(PhysDisp);
|
|
|
|
RtlInitUnicodeString(&DeviceName, PhysDisp->szWinDeviceName);
|
|
pstrDeviceName = &DeviceName;
|
|
|
|
if (lpDevMode != NULL)
|
|
{
|
|
#if DBG
|
|
//
|
|
// less debug output for DirectDraw :
|
|
//
|
|
|
|
oldTrace = GreTraceDisplayDriverLoad;
|
|
|
|
if (!bUpdateRegistry && !bSetMode) {
|
|
GreTraceDisplayDriverLoad &= 0xFFFFFFF0;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Get the new DEVMODE.
|
|
//
|
|
|
|
if (!NT_SUCCESS(DrvProbeAndCaptureDevmode(PhysDisp,
|
|
&pdevmodeInformation,
|
|
&bDetach,
|
|
lpDevMode,
|
|
FALSE,
|
|
PreviousMode,
|
|
bPrune,
|
|
bTryClosest,
|
|
FALSE)))
|
|
{
|
|
if (pdevmodeInformation)
|
|
{
|
|
VFREEMEM(pdevmodeInformation);
|
|
}
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvChangeDisplaySettings - Leave - Bad Devmode\n"));
|
|
return GRE_DISP_CHANGE_BADMODE;
|
|
}
|
|
|
|
|
|
if (lpDevMode->dmFields == 0)
|
|
bTryClosest = TRUE;
|
|
|
|
#if DBG
|
|
GreTraceDisplayDriverLoad = oldTrace;
|
|
#endif
|
|
}
|
|
else{ // For registry settings, always use closest
|
|
bTryClosest = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// At this point we have validated all data.
|
|
// So if the user just tested the mode, the call would now be successful
|
|
//
|
|
// Let's see if we actually need to do something with this mode.
|
|
//
|
|
|
|
//
|
|
// Write the data to the registry.
|
|
//
|
|
// This is not supported for the vgacompatible device - so it should just fail.
|
|
//
|
|
|
|
if (bUpdateRegistry && (PhysDisp != NULL) && (lpDevMode != NULL) && (gProtocolType == PROTOCOL_CONSOLE))
|
|
{
|
|
//
|
|
// Check if the Administrator has disabled this privilege.
|
|
// It is on by default.
|
|
//
|
|
|
|
disableAll = 0;
|
|
|
|
RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
|
|
L"GraphicsDrivers\\PermanentSettingChanges",
|
|
&QueryTable[0],
|
|
NULL,
|
|
NULL);
|
|
|
|
if (disableAll)
|
|
{
|
|
status = GRE_DISP_CHANGE_NOTUPDATED;
|
|
}
|
|
else
|
|
{
|
|
NTSTATUS retStatus = DrvUpdateDisplayDriverParameters(PhysDisp, pdevmodeInformation, bDetach, TRUE);
|
|
|
|
if (!NT_SUCCESS(retStatus))
|
|
{
|
|
status = GRE_DISP_CHANGE_BADMODE;
|
|
if (retStatus == STATUS_INVALID_PARAMETER_4)
|
|
{
|
|
status = GRE_DISP_CHANGE_BADPARAM;
|
|
}
|
|
}
|
|
}
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvChangeDisplaySettings - Save Params status %d\n", status));
|
|
}
|
|
|
|
//
|
|
// Set the mode dynamically
|
|
//
|
|
|
|
if (bSetMode && (status == GRE_DISP_CHANGE_SUCCESSFUL))
|
|
{
|
|
ASSERTGDI(pOrgMdev == NULL || DrvQueryMDEVPowerState(pOrgMdev),
|
|
"DrvChangeDisplaySettings called with powered off MDEV.\n");
|
|
|
|
#if MDEV_STACK_TRACE_LENGTH
|
|
LONG lMDEVTraceEntry, lMDEVTraceEntryNext;
|
|
ULONG StackEntries, UserStackEntry;
|
|
|
|
do
|
|
{
|
|
lMDEVTraceEntry = glMDEVTrace;
|
|
lMDEVTraceEntryNext = lMDEVTraceEntry + 1;
|
|
if (lMDEVTraceEntryNext >= gcMDEVTraceLength) lMDEVTraceEntryNext = 0;
|
|
} while (InterlockedCompareExchange(&glMDEVTrace, lMDEVTraceEntryNext, lMDEVTraceEntry) != lMDEVTraceEntry);
|
|
|
|
RtlZeroMemory(&gMDEVTrace[lMDEVTraceEntry],
|
|
sizeof(gMDEVTrace[lMDEVTraceEntry]));
|
|
gMDEVTrace[lMDEVTraceEntry].pMDEV = pOrgMdev;
|
|
gMDEVTrace[lMDEVTraceEntry].API = DrvChangeDisplaySettings_SetMode;
|
|
StackEntries = sizeof(gMDEVTrace[lMDEVTraceEntry].Trace)/sizeof(gMDEVTrace[lMDEVTraceEntry].Trace[0]);
|
|
UserStackEntry = RtlWalkFrameChain((PVOID *)gMDEVTrace[lMDEVTraceEntry].Trace,
|
|
StackEntries,
|
|
0);
|
|
StackEntries -= UserStackEntry;
|
|
RtlWalkFrameChain((PVOID *)&gMDEVTrace[lMDEVTraceEntry].Trace[UserStackEntry],
|
|
StackEntries,
|
|
1);
|
|
#endif
|
|
|
|
//
|
|
// First things first, acquire share dev lock. This must
|
|
// be acquired before any other dev locks are acquired.
|
|
//
|
|
|
|
GreAcquireSemaphoreEx(ghsemShareDevLock, SEMORDER_SHAREDEVLOCK, NULL);
|
|
CModeChangeInProgress mcip;
|
|
|
|
//
|
|
// Check if there are restrictions on changing the resolution
|
|
// dymanically
|
|
// Restriction don't apply for booting the machine though ...
|
|
//
|
|
|
|
pmdev = NULL;
|
|
status = GRE_DISP_CHANGE_FAILED;
|
|
disableAll = 0;
|
|
|
|
RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
|
|
L"GraphicsDrivers\\TemporarySettingChanges",
|
|
&QueryTable[0],
|
|
NULL,
|
|
NULL);
|
|
|
|
if ((disableAll == 0) || (pOrgMdev == NULL))
|
|
{
|
|
//
|
|
// Lets create a new MDEV
|
|
//
|
|
if (pOrgMdev)
|
|
{
|
|
PDEVOBJ pdoTmp(pOrgMdev->hdevParent);
|
|
ASSERTGDI(pdoTmp.cPdevRefs() > 10,
|
|
"curent MDEV is not the main one !\n");
|
|
|
|
//
|
|
// Dualview preprocess. We probe the registry first to see if
|
|
// the second view is about to be attached. The we notify the miniport
|
|
// before the first PDEV is created.
|
|
//
|
|
// If DUALVIEW attachment state changes, all PDEVs has to be regenerated.
|
|
// Even a PDEV with same settings cannot be reused.
|
|
//
|
|
// However, if there is D3D running, a PDEV cannot be simply regenerated
|
|
// due to ALT-TAB feature (a PDEV has an extra refcount for D3D apps). We
|
|
// cannot do anything but fail
|
|
//
|
|
switch (CheckAndNotifyDualView(pstrDeviceName, pOrgMdev, PreviousMode))
|
|
{
|
|
case DualviewNoSwitch:
|
|
gbInvalidateDualView = FALSE;
|
|
break;
|
|
case DualviewSwitch:
|
|
gbInvalidateDualView = TRUE;
|
|
bTryClosest = TRUE;
|
|
break;
|
|
default:
|
|
mcip.vDone();
|
|
GreReleaseSemaphoreEx(ghsemShareDevLock);
|
|
return GRE_DISP_CHANGE_BADDUALVIEW;
|
|
}
|
|
|
|
//
|
|
// Disable current MDEV.
|
|
//
|
|
// FALSE here, means ONLY meta PDEV will be disabled in
|
|
// multi monitor case. And hardware will not be disabled
|
|
// in single monitor cases.
|
|
//
|
|
|
|
if (DrvDisableMDEV(pOrgMdev, FALSE))
|
|
{
|
|
//
|
|
// Create new MDEV
|
|
//
|
|
// If we will create new HDEV which device is used
|
|
// in current (= old) MDEV. HDEV in old MDEV will
|
|
// be disabled, and stored in MDEV.Dev[x].Reserved
|
|
// for recover current config in error case later.
|
|
//
|
|
|
|
pmdev = DrvCreateMDEV(pstrDeviceName,
|
|
pdevmodeInformation,
|
|
pDesktopId,
|
|
0,
|
|
pOrgMdev,
|
|
KernelMode,
|
|
PruneFlag,
|
|
bTryClosest);
|
|
|
|
if (!pmdev)
|
|
{
|
|
|
|
// At this point, we have failed to create MDEV based on
|
|
// new configuration, so status continue to keep
|
|
// GRE_DISP_CHANGE_FAILED. And re-enable original MDEV.
|
|
//
|
|
|
|
DrvEnableMDEV(pOrgMdev, FALSE);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// At this point, we have all the hdevs involved in the
|
|
// switch.
|
|
//
|
|
// On entering this functions
|
|
// pOrgMdev - list of original hdevs
|
|
// pmdev - list of new hdevs
|
|
// reserved fields have the hdevs that were disabled
|
|
//
|
|
// On exit
|
|
// pmdev - list of old (permanent) and new hdevs for user
|
|
// pOrgMdev - unused
|
|
//
|
|
|
|
status = GRE_DISP_CHANGE_NO_CHANGE;
|
|
|
|
//
|
|
// Determine if anything changed in the two structures
|
|
//
|
|
|
|
if (pmdev->chdev != pOrgMdev->chdev)
|
|
{
|
|
status = GRE_DISP_CHANGE_SUCCESSFUL;
|
|
}
|
|
else
|
|
{
|
|
for (i=0 ; i <pmdev->chdev; i++)
|
|
{
|
|
if ((pmdev->Dev[i].hdev != pOrgMdev->Dev[i].hdev) ||
|
|
(!RtlEqualMemory(&(pmdev->Dev[i].rect),
|
|
&(pOrgMdev->Dev[i].rect),
|
|
sizeof(RECT))))
|
|
{
|
|
status = GRE_DISP_CHANGE_SUCCESSFUL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRACE_INIT(("DrvChangeDisplaySettings - no original MDEV, create one\n"));
|
|
|
|
//
|
|
// Dualview preprocess.
|
|
//
|
|
CheckAndNotifyDualView(pstrDeviceName, NULL, PreviousMode);
|
|
gbInvalidateDualView = TRUE;
|
|
|
|
pmdev = DrvCreateMDEV(pstrDeviceName,
|
|
pdevmodeInformation,
|
|
pDesktopId,
|
|
0,
|
|
NULL,
|
|
KernelMode,
|
|
PruneFlag,
|
|
bTryClosest);
|
|
|
|
if (pmdev)
|
|
{
|
|
status = GRE_DISP_CHANGE_SUCCESSFUL;
|
|
}
|
|
}
|
|
}
|
|
|
|
*pNewMdev = pmdev;
|
|
|
|
//
|
|
// If everything worked, but the two structures are not identical, do
|
|
// the switching around.
|
|
//
|
|
// Handle the four seperate case :
|
|
// 1-1, 1-many, many-1 and many-many
|
|
//
|
|
|
|
BOOL bSwitchError = FALSE;
|
|
|
|
HDEV hdevTmp;
|
|
HDEV hdevClone = NULL;
|
|
HDEV hdevCloned = NULL;
|
|
|
|
BOOL bSwitchParentAndChild = FALSE;
|
|
BOOL bEnableClone = FALSE;
|
|
|
|
if (status == GRE_DISP_CHANGE_SUCCESSFUL)
|
|
{
|
|
ULONG iClonehdev = 0;
|
|
HDEV *phdevDDLock = NULL;
|
|
HDEV ahdevDDLockQuick[7];
|
|
|
|
MULTIDEVLOCKOBJ mdloMdev;
|
|
MULTIDEVLOCKOBJ mdloOrgMdev;
|
|
|
|
HSEMAPHORE hsemCloneHdevDevLock = NULL;
|
|
HSEMAPHORE hsemOrgMdevDevLock = NULL;
|
|
HSEMAPHORE hsemOrgMdevPointer = NULL;
|
|
|
|
if (pOrgMdev)
|
|
{
|
|
BOOL bLockSemaphore = FALSE;
|
|
|
|
//
|
|
// Disable DirectDraw on both MDEV's. This must be done
|
|
// before we acquire the Devlock.
|
|
//
|
|
phdevDDLock = DrvDisableDirectDrawForModeChange(
|
|
pOrgMdev,pmdev,
|
|
(HDEV *)ahdevDDLockQuick,
|
|
sizeof(ahdevDDLockQuick)/sizeof(HDEV));
|
|
|
|
if (phdevDDLock == NULL)
|
|
{
|
|
bSwitchError = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The following lock rules must be abided, otherwise deadlocks may
|
|
// arise:
|
|
//
|
|
// o DevLock must be acquired after the ShareDevLock
|
|
// o Pointer lock must be acquired after Devlock (GreSetPointer);
|
|
// o RFont list lock must be acquired after Devlock (TextOut);
|
|
// o Handle manager lock must be acquired after Devlock (old
|
|
// CvtDFB2DIB);
|
|
// o Handle manager lock must be acquired after Palette Semaphore
|
|
// (GreSetPaletteEntries)
|
|
// o Palette Semaphore must be acquired after Devlock (BitBlt)
|
|
// o Palette Semaphore must be acquired after driver semaphore
|
|
// (bDeletePalette)
|
|
//
|
|
// So we acquire locks in the following order (note that the
|
|
// vAssertDynaLock() routines should be modified if this list ever
|
|
// changes):
|
|
//
|
|
|
|
//
|
|
// At this point, we have *not* ssyned devlock for new MDEV, yet,
|
|
// so lock all children. During the mode change. we don't want to
|
|
// anything change in its children.
|
|
//
|
|
|
|
mdloOrgMdev.vInit(pOrgMdev);
|
|
mdloMdev.vInit(pmdev);
|
|
|
|
if (mdloMdev.bValid() && mdloOrgMdev.bValid())
|
|
{
|
|
PDEVOBJ pdoTmp;
|
|
|
|
pdoTmp.vInit(pOrgMdev->hdevParent);
|
|
hsemOrgMdevDevLock = pdoTmp.hsemDevLock();
|
|
hsemOrgMdevPointer = pdoTmp.hsemPointer();
|
|
|
|
//
|
|
// Lock the parent of MDEV.
|
|
//
|
|
|
|
GreAcquireSemaphoreEx(hsemOrgMdevDevLock, SEMORDER_DEVLOCK, NULL);
|
|
|
|
//
|
|
// Lock the pointer in old mdev's parent.
|
|
//
|
|
|
|
GreAcquireSemaphoreEx(hsemOrgMdevPointer, SEMORDER_POINTER, hsemOrgMdevDevLock);
|
|
|
|
//
|
|
// Lock the children in MDEVs
|
|
//
|
|
|
|
mdloOrgMdev.vLock(); // No drawing to any dynamic surfaces
|
|
mdloMdev.vLock(); // No drawing to any dynamic surfaces
|
|
|
|
// WinBug #301042 3-1-2001 jasonha
|
|
// GDI: SPRITESTATE duplicated and inconsistent when cloning
|
|
// When cloning is used two PDEV (and therefore two SPRITESTATEs)
|
|
// try to manage the same display (psoScreen). Hooking one
|
|
// while 'Inside' the other will confuse the state. Hooking
|
|
// will only change if there are visible sprites; so we
|
|
// will temporarily hide them all. Later we will unhide all
|
|
// sprites on the whichever MDEV will remain active.
|
|
//
|
|
// Hide all sprites
|
|
//
|
|
|
|
vSpHideSprites(pOrgMdev->hdevParent, TRUE);
|
|
|
|
//
|
|
// Fixup new and old MDEVs.
|
|
//
|
|
|
|
if ((pmdev->chdev == 1) && (pOrgMdev->chdev != 1))
|
|
{
|
|
//
|
|
// Many to 1
|
|
//
|
|
|
|
for (i=0 ; i <pOrgMdev->chdev; i++)
|
|
{
|
|
if (pOrgMdev->Dev[i].hdev == pmdev->Dev[0].hdev)
|
|
{
|
|
TRACE_INIT(("DrvChangeDisplaySettings: creating clone\n"));
|
|
|
|
//
|
|
// DrvCreateCloneHDEV will creates exactly same HDEV with
|
|
// different handle.
|
|
//
|
|
|
|
hdevClone = DrvCreateCloneHDEV(
|
|
pmdev->Dev[0].hdev,
|
|
DRV_CLONE_DEREFERENCE_ORG);
|
|
|
|
if (hdevClone)
|
|
{
|
|
//
|
|
// Replace the hdev with clone, and save original
|
|
// into reserved fields for later to back out
|
|
// mode change if error happens.
|
|
//
|
|
|
|
pOrgMdev->Dev[i].hdev = hdevClone;
|
|
pOrgMdev->Dev[i].Reserved = pmdev->Dev[0].hdev;
|
|
|
|
hdevCloned = pmdev->Dev[0].hdev;
|
|
iClonehdev = i;
|
|
}
|
|
else
|
|
{
|
|
bSwitchError = TRUE;
|
|
}
|
|
|
|
//
|
|
// Shouldn't be another same HDEV in MDEV,
|
|
// so, just go out loop here...
|
|
//
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if ((pmdev->chdev != 1) && (pOrgMdev->chdev == 1))
|
|
{
|
|
//
|
|
// 1 to Many
|
|
//
|
|
|
|
//
|
|
// [Case 1] - Attach new monitor, and some change has been made on existing
|
|
//
|
|
// At this case, new MDEV will everything new HDEV, like...
|
|
//
|
|
// Org MDEV - ATI: 800x600 8bpp (HDEV is A)
|
|
//
|
|
// New MDEV - ATI: 1024x768 24bpp (HDEV is B)
|
|
// MGA: 1024x768 32bpp (HDEV is C)
|
|
// Parent: Created based on B and C (HDEV is D)
|
|
// then,
|
|
//
|
|
// 1) mode change between "A" and "D".
|
|
// 2) flip handle (set "A" to where "D" is, and set "D" to where "A" is).
|
|
//
|
|
// So that finally "A" becomes a parent of "B" and "C". then delete "D".
|
|
//
|
|
// [Case 2] - Attach new monitor.
|
|
//
|
|
// At this case, new MDEV will contains previous HDEV, since original
|
|
// monitor is nothing changed, so it looks like...
|
|
//
|
|
// Org MDEV - ATI: 1024x768 24bpp (HDEV is A)
|
|
//
|
|
// New MDEV - ATI: 1024x768 24bpp (HDEV is A)
|
|
// MGA: 1024x768 32bpp (HDEV is C)
|
|
// Parent: Created based on A and C (HDEV is D)
|
|
//
|
|
// In this case, we can *not* mode change, because if we do mode
|
|
// change between "A" and "D", then we flip the handle, "A"
|
|
// becomes a parent of "A" and "C" (since when we create "D", it
|
|
// children are "A" and "C". when we create parent, parent can know
|
|
// and cache into thier local, who is thier children.
|
|
// (see Multi.cxx MulEnablePDEV()) "A" can not be a parent of "A".
|
|
//
|
|
// Thus, how we do there is just create a clone (= "B") of "A", and replace
|
|
// "A" with "B" in new MDEV, so new MDEV will be like,
|
|
//
|
|
// New MDEV - ATI: 1024x768 24bpp (HDEV is B) (clone of A)
|
|
// MGA: 1024x768 32bpp (HDEV is C)
|
|
// Parent: Created based on B and C (HDEV is D)
|
|
//
|
|
// Of course we can do notify to parent driver to one of thier
|
|
// child "A" is replaced with "D" and do same for new MDEV, to
|
|
// avoid to create clone. But it too complex to re-initialized
|
|
// parent "A" based on "D" and "C", and what-else will problem later on.
|
|
// So here we just create a clone, so that we can take a same
|
|
// code path as 1) except create a clone, here.
|
|
//
|
|
// Then, (actually here is same as case 1)
|
|
//
|
|
// 1) mode change between "A" and "D".
|
|
// 2) flip handle (set "A" to where "D" is, and set "D" to where "A" is).
|
|
//
|
|
// So that finally "A" becomes a parent of "B" and "C". then delete "D".
|
|
//
|
|
|
|
//
|
|
// Do we have any re-used original HDEV in new MDEV ?
|
|
//
|
|
|
|
for (i=0 ; i <pmdev->chdev; i++)
|
|
{
|
|
if (pmdev->Dev[i].hdev == pOrgMdev->Dev[0].hdev)
|
|
{
|
|
TRACE_INIT(("DrvChangeDisplaySettings: creating clone\n"));
|
|
|
|
//
|
|
// DrvCreateCloneHDEV will creates exactly same HDEV with
|
|
// different handle.
|
|
//
|
|
|
|
hdevClone = DrvCreateCloneHDEV(
|
|
pOrgMdev->Dev[0].hdev,
|
|
DRV_CLONE_DEREFERENCE_ORG);
|
|
|
|
if (hdevClone)
|
|
{
|
|
//
|
|
// Replace the hdev with clone, and save original
|
|
// into reserved fields for later to back out
|
|
// mode change if error happens.
|
|
//
|
|
|
|
pmdev->Dev[i].hdev = hdevClone;
|
|
pmdev->Dev[i].Reserved = pOrgMdev->Dev[0].hdev;
|
|
|
|
hdevCloned = pOrgMdev->Dev[0].hdev;
|
|
|
|
//
|
|
// Later we need copy data to Clone from Cloned.
|
|
//
|
|
bEnableClone = TRUE;
|
|
}
|
|
else
|
|
{
|
|
bSwitchError = TRUE;
|
|
}
|
|
|
|
//
|
|
// Shouldn't be another same HDEV in MDEV,
|
|
// so, just go out loop here...
|
|
//
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bSwitchError == FALSE)
|
|
{
|
|
//
|
|
// We will switch between parent and children
|
|
//
|
|
|
|
bSwitchParentAndChild = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// 1 to 1, Many to Many
|
|
//
|
|
}
|
|
|
|
if (hdevClone)
|
|
{
|
|
pdoTmp.vInit(hdevClone);
|
|
hsemCloneHdevDevLock = pdoTmp.hsemDevLock();
|
|
|
|
//
|
|
// If we creates any clone device, hold devlock here.
|
|
//
|
|
|
|
GreAcquireSemaphoreEx(hsemCloneHdevDevLock, SEMORDER_DEVLOCK, NULL);
|
|
}
|
|
|
|
if (bSwitchError == FALSE)
|
|
{
|
|
//
|
|
// Lock rest of semaphores which we should hold during mode change.
|
|
//
|
|
|
|
GreAcquireSemaphoreEx(ghsemDriverMgmt, SEMORDER_DRIVERMGMT, NULL); // No driver loading/unloading
|
|
GreAcquireSemaphoreEx(ghsemPalette, SEMORDER_DRIVERMGMT, NULL); // No SaveDC/RestoreDC
|
|
GreAcquireSemaphoreEx(ghsemPublicPFT, SEMORDER_PUBLICPFT, NULL); // Nobody else uses the font table
|
|
GreAcquireSemaphoreEx(ghsemRFONTList, SEMORDER_RFONTLIST, NULL); // Nobody else uses the RFONT list
|
|
|
|
//
|
|
// Lock handle manager to prevent new handle being created, or
|
|
// deleting existing handle. so that we can safely walk through
|
|
// handle manager list.
|
|
//
|
|
|
|
GreAcquireHmgrSemaphore();
|
|
|
|
bLockSemaphore = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bSwitchError = TRUE;
|
|
}
|
|
}
|
|
|
|
if (bSwitchError == FALSE)
|
|
{
|
|
ASSERTGDI(bLockSemaphore == TRUE,
|
|
"DrvChangeDisplaySettings(): Semaphores is not locked\n");
|
|
|
|
//
|
|
// Do the mode change for children device.
|
|
//
|
|
|
|
if (pmdev->chdev == 1)
|
|
{
|
|
if (pOrgMdev->chdev == 1)
|
|
{
|
|
//
|
|
// 1 to 1
|
|
//
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvChangeDisplaySettings: Mode Change 1 -> 1.\n"));
|
|
|
|
if (bDynamicModeChange(pOrgMdev->Dev[0].hdev,
|
|
pmdev->Dev[0].hdev) == TRUE)
|
|
{
|
|
hdevTmp = pOrgMdev->Dev[0].hdev;
|
|
pOrgMdev->Dev[0].hdev = pmdev->Dev[0].hdev;
|
|
pmdev->Dev[0].hdev = hdevTmp;
|
|
}
|
|
else
|
|
{
|
|
// Error occured
|
|
bSwitchError = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Many to 1
|
|
//
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvChangeDisplaySettings: Mode Change Many -> 1.\n"));
|
|
|
|
if (bSwitchError == FALSE)
|
|
{
|
|
#ifdef DONT_CHECKIN
|
|
if (hdevClone)
|
|
{
|
|
//
|
|
// Transfer DC_TYPE objects to clone.
|
|
//
|
|
|
|
DrvTransferGdiObjects(pOrgMdev->Dev[iClonehdev].hdev,
|
|
pmdev->Dev[0].hdev,
|
|
DRV_TRANS_DC_TYPE |
|
|
DRV_TRANS_TO_CLONE);
|
|
}
|
|
#endif
|
|
|
|
if (bDynamicModeChange(pOrgMdev->hdevParent,
|
|
pmdev->Dev[0].hdev) == TRUE)
|
|
{
|
|
hdevTmp = pOrgMdev->hdevParent;
|
|
pOrgMdev->hdevParent = pmdev->Dev[0].hdev;
|
|
pmdev->Dev[0].hdev = hdevTmp;
|
|
|
|
if (hdevClone)
|
|
{
|
|
hdevCloned = hdevTmp;
|
|
|
|
#ifdef DONT_CHECKIN
|
|
//
|
|
// Transfer back DC_TYPE objects in clone to original.
|
|
//
|
|
|
|
DrvTransferGdiObjects(pmdev->Dev[0].hdev,
|
|
pOrgMdev->Dev[iClonehdev].hdev,
|
|
DRV_TRANS_DC_TYPE);
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Error occured
|
|
bSwitchError = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pOrgMdev->chdev == 1)
|
|
{
|
|
//
|
|
// 1 to Many
|
|
//
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvChangeDisplaySettings: Mode Change 1 -> Many.\n"));
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Many to Many
|
|
//
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvChangeDisplaySettings: Mode Change Many -> Many.\n"));
|
|
|
|
for (i=0 ; i <pmdev->chdev; i++)
|
|
{
|
|
PDEVOBJ pdo1(pmdev->Dev[i].hdev);
|
|
|
|
for (j=0 ; j <pOrgMdev->chdev; j++)
|
|
{
|
|
PDEVOBJ pdo2(pOrgMdev->Dev[j].hdev);
|
|
|
|
if (pdo1.ppdev->pGraphicsDevice == pdo2.ppdev->pGraphicsDevice)
|
|
{
|
|
if (pmdev->Dev[i].hdev == pOrgMdev->Dev[j].hdev)
|
|
{
|
|
// Same hdev, nothing need to do.
|
|
}
|
|
else if (bDynamicModeChange(pOrgMdev->Dev[j].hdev,
|
|
pmdev->Dev[i].hdev) == TRUE)
|
|
{
|
|
hdevTmp = pOrgMdev->Dev[j].hdev;
|
|
pOrgMdev->Dev[j].hdev = pmdev->Dev[i].hdev;
|
|
pmdev->Dev[i].hdev = hdevTmp;
|
|
}
|
|
else
|
|
{
|
|
// Error occured
|
|
bSwitchError = TRUE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Release handle manager lock.
|
|
//
|
|
|
|
GreReleaseHmgrSemaphore();
|
|
|
|
//
|
|
// Now, we are finished all mode changes for parent and children
|
|
// so we are safe to unlock it.
|
|
//
|
|
|
|
GreReleaseSemaphoreEx(ghsemRFONTList);
|
|
GreReleaseSemaphoreEx(ghsemPublicPFT);
|
|
GreReleaseSemaphoreEx(ghsemPalette);
|
|
GreReleaseSemaphoreEx(ghsemDriverMgmt);
|
|
|
|
//
|
|
// Mark as unlocked.
|
|
//
|
|
|
|
bLockSemaphore = FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we need to create a new parent and do a switch, now is
|
|
// the time.
|
|
//
|
|
|
|
if (bSwitchError == FALSE)
|
|
{
|
|
//
|
|
// Setup the parent hdev for old MDEV (if old MDEV is provided).
|
|
//
|
|
|
|
if (pOrgMdev)
|
|
{
|
|
if (pOrgMdev->chdev == 1)
|
|
{
|
|
//
|
|
// 1 to 1, 1 to Many.
|
|
//
|
|
|
|
pOrgMdev->hdevParent = pOrgMdev->Dev[0].hdev;
|
|
pOrgMdev->Reserved = pOrgMdev->Dev[0].Reserved; // actually not nessesary...
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Many to 1, Many to Many.
|
|
//
|
|
}
|
|
}
|
|
|
|
//
|
|
// Setup the parent hdev for new MDEV.
|
|
//
|
|
|
|
if (pmdev->chdev == 1)
|
|
{
|
|
//
|
|
// 1 to 1, Many to 1.
|
|
//
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvCompleteMDEV: Single Device\n"));
|
|
|
|
pmdev->hdevParent = pmdev->Dev[0].hdev;
|
|
pmdev->Reserved = pmdev->Dev[0].Reserved;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// 1 to Many, Many to Many.
|
|
//
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvChangeDisplaySettings: Create new Parent MDEV (Many/1 -> Many)\n"));
|
|
|
|
//
|
|
// If we have more than one device that is attached to the
|
|
// desktop, then we need to create the META structure for
|
|
// that device, initialize it, and use that as the primary
|
|
// device.
|
|
//
|
|
|
|
DRV_NAMES drvName;
|
|
HDEV hdevDisabled;
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvCompleteMDEV: Create HMDEV\n"));
|
|
|
|
drvName.cNames = 1;
|
|
drvName.D[0].hDriver = NULL;
|
|
drvName.D[0].lpDisplayName = (LPWSTR)MulEnableDriver;
|
|
|
|
pmdev->hdevParent = hCreateHDEV((PGRAPHICS_DEVICE) DDML_DRIVER,
|
|
&drvName,
|
|
((PDEVMODEW)(pmdev)),
|
|
pmdev->pDesktopId,
|
|
DRIVER_CAPABLE_ALL,
|
|
DRIVER_ACCELERATIONS_FULL,
|
|
TRUE, // ignored in this case
|
|
GCH_DDML,
|
|
&hdevDisabled);
|
|
|
|
if (!pmdev->hdevParent)
|
|
{
|
|
RIP("Drv_Trace: DrvCompleteMDEV: DDML failed");
|
|
bSwitchError = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (pOrgMdev != NULL)
|
|
{
|
|
PDEVOBJ pdoParent(pmdev->hdevParent);
|
|
HSEMAPHORE hsemParentDevLock = pdoParent.hsemDevLock();
|
|
|
|
//
|
|
// During the mode change devlock should be hold for parent
|
|
//
|
|
|
|
GreAcquireSemaphoreEx(hsemParentDevLock, SEMORDER_DEVLOCK, NULL);
|
|
GreAcquireSemaphoreEx(ghsemDriverMgmt, SEMORDER_DRIVERMGMT, NULL);
|
|
GreAcquireSemaphoreEx(ghsemPalette, SEMORDER_PALETTE, NULL);
|
|
GreAcquireSemaphoreEx(ghsemPublicPFT, SEMORDER_PUBLICPFT, NULL); // Nobody else uses the font table
|
|
GreAcquireSemaphoreEx(ghsemRFONTList, SEMORDER_RFONTLIST, NULL);
|
|
GreAcquireHmgrSemaphore();
|
|
|
|
if (bSwitchParentAndChild)
|
|
{
|
|
//
|
|
// Mode change between new parent (meta) HDEV and
|
|
// old HDEV, so that old HDEV becomes new parent
|
|
// (meta) HDEV which has children.
|
|
//
|
|
|
|
if (bDynamicModeChange(pOrgMdev->Dev[0].hdev,
|
|
pmdev->hdevParent) == TRUE)
|
|
{
|
|
hdevTmp = pmdev->hdevParent;
|
|
pmdev->hdevParent = pOrgMdev->Dev[0].hdev;
|
|
pOrgMdev->hdevParent = hdevTmp;
|
|
pOrgMdev->Dev[0].hdev = hdevTmp;
|
|
|
|
if (hdevClone)
|
|
{
|
|
hdevCloned = hdevTmp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Error occured
|
|
bSwitchError = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Switch the mode between parents (DDML).
|
|
//
|
|
|
|
|
|
if (bDynamicModeChange(pOrgMdev->hdevParent,
|
|
pmdev->hdevParent) == TRUE)
|
|
{
|
|
hdevTmp = pOrgMdev->hdevParent;
|
|
pOrgMdev->hdevParent = pmdev->hdevParent;
|
|
pmdev->hdevParent = hdevTmp;
|
|
}
|
|
else
|
|
{
|
|
// Error occured
|
|
bSwitchError = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Release semaphores.
|
|
//
|
|
|
|
GreReleaseHmgrSemaphore();
|
|
GreReleaseSemaphoreEx(ghsemRFONTList);
|
|
GreReleaseSemaphoreEx(ghsemPublicPFT); // Nobody else uses the font table
|
|
GreReleaseSemaphoreEx(ghsemPalette);
|
|
GreReleaseSemaphoreEx(ghsemDriverMgmt);
|
|
GreReleaseSemaphoreEx(hsemParentDevLock);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Below code is only for mode change case.
|
|
//
|
|
|
|
if (pOrgMdev)
|
|
{
|
|
//
|
|
// Finalize clone hdev stuff (if created)
|
|
//
|
|
|
|
if ((bSwitchError == FALSE) && hdevClone && hdevCloned)
|
|
{
|
|
PDEV *pdevNew = (PDEV *) hdevClone;
|
|
PDEV *pdevOld = (PDEV *) hdevCloned;
|
|
|
|
PDEVOBJ pdoNew(hdevClone);
|
|
PDEVOBJ pdoOld(hdevCloned);
|
|
|
|
ASSERTGDI(pdevOld->pSurface != NULL,
|
|
"DrvChangeDisplaySettings():Original has null surface\n");
|
|
|
|
ASSERTGDI(pdevNew->pSurface == pdevOld->pSurface,
|
|
"DrvChangeDisplaySettings():Clone has wrong surface\n");
|
|
|
|
ASSERTGDI(pdevNew->dhpdev == pdevOld->dhpdev,
|
|
"DrvChangeDisplaySettings():Clone has wrong dhpdev\n");
|
|
|
|
ASSERTGDI(!pdoOld.bCloneDriver(),
|
|
"DrvChangeDisplaySettings():Original should not marked as clone\n");
|
|
|
|
ASSERTGDI(pdoNew.bCloneDriver(),
|
|
"DrvChangeDisplaySettings():Clone should have marked as clone\n");
|
|
|
|
ASSERTGDI(pdevOld->pGraphicsDevice == pdevNew->pGraphicsDevice,
|
|
"DrvChangeDisplaySettings():Clone should have same graphics device\n");
|
|
|
|
if (bEnableClone)
|
|
{
|
|
//
|
|
// Inform the driver that the PDEV is complete.
|
|
//
|
|
|
|
TRACE_INIT(("DrvChangeDisplaySettings(): Enabling clone pdev\n"));
|
|
|
|
//
|
|
// Transfer device surface to clone from cloned PDEV.
|
|
//
|
|
|
|
pdevNew->pSurface = pdevOld->pSurface;
|
|
|
|
//
|
|
// Set new hdev into surface.
|
|
//
|
|
|
|
if (pdevNew->pSurface)
|
|
{
|
|
pdevNew->pSurface->hdev(pdoNew.hdev());
|
|
}
|
|
|
|
//
|
|
// Transsfer dhpdev from cloned to clone
|
|
//
|
|
|
|
pdevNew->dhpdev = pdevOld->dhpdev;
|
|
|
|
//
|
|
// Transfer DirectDraw driver state from cloned to clone.
|
|
//
|
|
|
|
DxDdDynamicModeChange(pdoOld.hdev(), pdoNew.hdev(), DXG_MODECHANGE_TRANSFER);
|
|
|
|
//
|
|
// Transfer other surface and other objects which own by hdevCloned to hdevClone.
|
|
//
|
|
// Transfer the objects belonging to cloned pdev to clone,
|
|
// transfer between clone and cloned is completely seemless
|
|
// from driver. So we need to change owner of those object
|
|
// by ourselves.
|
|
//
|
|
|
|
DrvTransferGdiObjects(pdoNew.hdev(),pdoOld.hdev(),DRV_TRANS_ALL_TYPE);
|
|
|
|
//
|
|
// Invalidate surface in cloned pdev, so that this won't deleted.
|
|
//
|
|
|
|
pdevOld->pSurface = NULL;
|
|
|
|
//
|
|
// Invalidate dhpdev in cloned.
|
|
//
|
|
|
|
pdevOld->dhpdev = NULL;
|
|
|
|
//
|
|
// Now, clone device has everything grab from original, so
|
|
// mark original as clone, and mark clone as original.
|
|
//
|
|
|
|
pdoOld.bCloneDriver(TRUE);
|
|
pdoNew.bCloneDriver(FALSE);
|
|
|
|
//
|
|
// Disabling old pdev.
|
|
//
|
|
|
|
pdoOld.bDisabled(TRUE);
|
|
|
|
//
|
|
// call device driver to let them knows new hdev.
|
|
//
|
|
|
|
pdoNew.CompletePDEV(pdoNew.dhpdev(),pdoNew.hdev());
|
|
|
|
//
|
|
// Make sure pdoOld which going to be delete, does not have any reference.
|
|
//
|
|
|
|
// KdPrint(("GDI DDML: 1 to Many monitor video mode change\n"));
|
|
// KdPrint(("GDI DDML: Open ref count in ppdevOld = %d\n", pdevOld->cPdevOpenRefs));
|
|
// KdPrint(("GDI DDML: Pdev ref count in ppdevOld = %d\n", pdevOld->cPdevRefs));
|
|
}
|
|
else
|
|
{
|
|
TRACE_INIT(("DrvChangeDisplaySettings(): Disabling clone pdev\n"));
|
|
|
|
//
|
|
// Clone was created for temporary purpose.
|
|
//
|
|
// Invalidate surface in clone pdev, so that this won't deleted.
|
|
//
|
|
|
|
pdevNew->pSurface = NULL;
|
|
|
|
//
|
|
// Disable clone PDEV.
|
|
//
|
|
|
|
pdoNew.bDisabled(TRUE);
|
|
|
|
// KdPrint(("GDI DDML: Many to 1 monitor video mode change\n"));
|
|
// KdPrint(("GDI DDML: Open ref count in ppdevOld = %d\n", pdevNew->cPdevOpenRefs));
|
|
// KdPrint(("GDI DDML: Pdev ref count in ppdevOld = %d\n", pdevNew->cPdevRefs));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Disable old parant pdev if its meta driver.
|
|
// Since meta PDEV never be reused in new MDEV.
|
|
//
|
|
|
|
PDEVOBJ pdoOldParent(pOrgMdev->hdevParent);
|
|
|
|
if (pdoOldParent.bMetaDriver())
|
|
{
|
|
pdoOldParent.bDisabled(TRUE);
|
|
}
|
|
|
|
//
|
|
// Reactivate sprites on whichever MDEV will be used
|
|
//
|
|
|
|
vSpHideSprites(((bSwitchError == FALSE) ? pmdev : pOrgMdev)->hdevParent, FALSE);
|
|
|
|
//
|
|
// If we hold clone's devlock, now safe to release.
|
|
//
|
|
|
|
if (hsemCloneHdevDevLock)
|
|
{
|
|
GreReleaseSemaphoreEx(hsemCloneHdevDevLock);
|
|
}
|
|
|
|
//
|
|
// Release MDEV's lock.
|
|
//
|
|
|
|
mdloMdev.vUnlock();
|
|
mdloOrgMdev.vUnlock();
|
|
}
|
|
|
|
//
|
|
// Finalize new MDEV.
|
|
//
|
|
|
|
if (bSwitchError == FALSE)
|
|
{
|
|
PDEVOBJ poP(pmdev->hdevParent);
|
|
|
|
if (pmdev->chdev != 1)
|
|
{
|
|
HDEV hdevPalette;
|
|
|
|
//
|
|
// Do devlock sharing
|
|
//
|
|
|
|
DrvSetSharedDevLock(pmdev);
|
|
|
|
//
|
|
// Do palette sharing
|
|
//
|
|
|
|
hdevPalette = DrvSetSharedPalette(pmdev);
|
|
|
|
if (!poP.bIsPalManaged() && hdevPalette)
|
|
{
|
|
//
|
|
// If parent is not pal-managed, but if there is any
|
|
// pal-managed device, realize halftone palette on there.
|
|
//
|
|
|
|
DrvRealizeHalftonePalette(hdevPalette,TRUE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// For single monitor case, make sure ppdevParent points itself.
|
|
//
|
|
|
|
PPDEV ppdev = (PPDEV) poP.hdev();
|
|
XEPALOBJ palObj(poP.ppalSurf());
|
|
|
|
if (ppdev->ppdevParent != ppdev)
|
|
{
|
|
WARNING("ChangeDisplaySettings: ppdev->parent != ppdev, fixing it\n");
|
|
|
|
ppdev->ppdevParent = ppdev;
|
|
}
|
|
|
|
//
|
|
// Correct palette pointer to driver entry.
|
|
//
|
|
|
|
poP.pfnSetPalette(PPFNDRV(poP, SetPalette));
|
|
|
|
//
|
|
// Unshared palette table entry.
|
|
//
|
|
|
|
palObj.apalResetColorTable();
|
|
}
|
|
}
|
|
|
|
if (hsemOrgMdevDevLock)
|
|
{
|
|
ASSERTGDI(hsemOrgMdevPointer,
|
|
"DrvChangeDisplaySettings():Pointer Lock is NULL\n");
|
|
|
|
if (bSwitchError == FALSE)
|
|
{
|
|
PDEVOBJ pdoParent(pmdev->hdevParent);
|
|
|
|
//
|
|
// If mode change has been successfully done, make sure the primary
|
|
// devlock has not been changed through the mode change.
|
|
//
|
|
|
|
ASSERTGDI(hsemOrgMdevDevLock == pdoParent.hsemDevLock(),
|
|
"DrvChangeDisplaySettings():DevLock has been changed !\n");
|
|
}
|
|
|
|
//
|
|
// Unlock the pointer
|
|
//
|
|
|
|
GreReleaseSemaphoreEx(hsemOrgMdevPointer);
|
|
|
|
//
|
|
// Unlock the parent of MDEV.
|
|
//
|
|
|
|
GreReleaseSemaphoreEx(hsemOrgMdevDevLock);
|
|
}
|
|
|
|
//
|
|
// Resume DirectDraw, this can not do while devlock is hold
|
|
//
|
|
|
|
if (phdevDDLock)
|
|
{
|
|
DrvEnableDirectDrawForModeChange(
|
|
phdevDDLock,
|
|
phdevDDLock != ahdevDDLockQuick);
|
|
}
|
|
|
|
//
|
|
// If we succeed, return the new data strucutre.
|
|
//
|
|
|
|
if (bSwitchError == FALSE)
|
|
{
|
|
PDEVOBJ pdo;
|
|
|
|
//
|
|
// Dump the old MDEV structure.
|
|
//
|
|
|
|
if (pOrgMdev != NULL)
|
|
{
|
|
TRACE_INIT(("OLD pmdev = %08lx\n", pOrgMdev));
|
|
|
|
TRACE_INIT(("\tOLD hdevParent = %08lx\n", pOrgMdev->hdevParent));
|
|
TRACE_INIT(("\tOLD chdev = %d\n", pOrgMdev->chdev));
|
|
|
|
for (i = 0; i < pOrgMdev->chdev; i++)
|
|
{
|
|
pdo.vInit(pOrgMdev->Dev[i].hdev);
|
|
|
|
TRACE_INIT(("\tOLD [%d].hdev = %08lx\n", i, pOrgMdev->Dev[i].hdev));
|
|
|
|
ASSERTGDI(pdo.ppdev->pGraphicsDevice != (PGRAPHICS_DEVICE) DDML_DRIVER,
|
|
"OLD MDEV has DDML_DRIVER as child hdev");
|
|
|
|
//
|
|
// Unmark old MDEV as part of the desktop.
|
|
//
|
|
|
|
pdo.ppdev->pGraphicsDevice->stateFlags &= ~DISPLAY_DEVICE_ATTACHED_TO_DESKTOP;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Clear the primary flag
|
|
//
|
|
|
|
for (PGRAPHICS_DEVICE PhysDispTemp = gpGraphicsDeviceList;
|
|
PhysDispTemp != NULL;
|
|
PhysDispTemp = PhysDispTemp->pNextGraphicsDevice)
|
|
{
|
|
PhysDispTemp->stateFlags &= ~DISPLAY_DEVICE_PRIMARY_DEVICE;
|
|
}
|
|
|
|
//
|
|
// Dump the new MDEV structure.
|
|
//
|
|
|
|
TRACE_INIT(("NEW pmdev = %08lx\n", pmdev));
|
|
|
|
TRACE_INIT(("\tNEW hdevParent = %08lx\n", pmdev->hdevParent));
|
|
TRACE_INIT(("\tNEW chdev = %d\n", pmdev->chdev));
|
|
|
|
for (i = 0; i < pmdev->chdev; i++)
|
|
{
|
|
pdo.vInit(pmdev->Dev[i].hdev);
|
|
PDEVMODEW pdm = pdo.ppdev->ppdevDevmode;
|
|
|
|
TRACE_INIT(("\tNEW [%d].hdev = %08lx\n", i, pmdev->Dev[i].hdev));
|
|
TRACE_INIT(("\tNEW [%d].rect = %d, %d, %d, %d,\n", i,
|
|
pmdev->Dev[i].rect.left, pmdev->Dev[i].rect.top,
|
|
pmdev->Dev[i].rect.right, pmdev->Dev[i].rect.bottom));
|
|
|
|
ASSERTGDI(pdo.ppdev->pGraphicsDevice != (PGRAPHICS_DEVICE) DDML_DRIVER,
|
|
"NEW MDEV has DDML_DRIVER as child hdev");
|
|
|
|
//
|
|
// Mark new MDEV as part of the desktop.
|
|
//
|
|
|
|
pdo.ppdev->pGraphicsDevice->stateFlags |= DISPLAY_DEVICE_ATTACHED_TO_DESKTOP;
|
|
|
|
//
|
|
// Adjust the position and mark the primary device
|
|
//
|
|
|
|
pdm->dmPosition.x = pmdev->Dev[i].rect.left;
|
|
pdm->dmPosition.y = pmdev->Dev[i].rect.top;
|
|
|
|
if ((pdm->dmPosition.x == 0) &&
|
|
(pdm->dmPosition.y == 0))
|
|
{
|
|
pdo.ppdev->pGraphicsDevice->stateFlags |= DISPLAY_DEVICE_PRIMARY_DEVICE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the DeviceCaps for the new HDEV
|
|
//
|
|
|
|
GreUpdateSharedDevCaps(pmdev->hdevParent);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We had a problem with the mode switch.
|
|
// Revert to the old configuration.
|
|
//
|
|
// We must make sure pOrgMdev is still what is
|
|
// currently active.
|
|
//
|
|
// ISSUE: Is this multmon safe ?? shared devlock ??
|
|
// ISSUE: Is this clone-pdev safe ?? (of course, no).
|
|
|
|
DrvBackoutMDEV(pmdev);
|
|
VFREEMEM(pmdev);
|
|
*pNewMdev = NULL;
|
|
|
|
if (pOrgMdev != NULL)
|
|
{
|
|
DrvEnableMDEV(pOrgMdev, FALSE);
|
|
}
|
|
|
|
gcFailedModeChanges++;
|
|
|
|
status = GRE_DISP_CHANGE_RESTART;
|
|
}
|
|
}
|
|
else if (status == GRE_DISP_CHANGE_NO_CHANGE)
|
|
{
|
|
//
|
|
// If nothing has been changed, just copy Parent HDEV from original.
|
|
//
|
|
|
|
pmdev->hdevParent = pOrgMdev->hdevParent;
|
|
pmdev->Reserved = pOrgMdev->Reserved;
|
|
|
|
//
|
|
// If parent HDEV is DDML, Increment OpenRef count. otheriwse parent
|
|
// hdev is same as its child, so OpenRef is already incremented in
|
|
// hCreateHDEV().
|
|
//
|
|
|
|
if (pmdev->chdev > 1)
|
|
{
|
|
GreAcquireSemaphoreEx(ghsemDriverMgmt, SEMORDER_DRIVERMGMT, NULL);
|
|
|
|
PDEVOBJ pdo(pmdev->hdevParent);
|
|
|
|
pdo.ppdev->cPdevOpenRefs++;
|
|
pdo.ppdev->cPdevRefs++;
|
|
|
|
GreReleaseSemaphoreEx(ghsemDriverMgmt);
|
|
}
|
|
|
|
ASSERTGDI(pmdev->chdev == pOrgMdev->chdev,
|
|
"DrvChangeDisplaySettings():Status is no change, but chdev is different\n");
|
|
ASSERTGDI(pmdev->pDesktopId == pOrgMdev->pDesktopId,
|
|
"DrvChangeDisplaySettings():Status is no change, but pDesktopId is different\n");
|
|
}
|
|
|
|
//
|
|
// Reenable the HDEVs (whether we fail or succeed) so
|
|
// DirectDraw and other functionality is reestablished.
|
|
//
|
|
// If the call succeeded, the destroy the old HDEVs
|
|
//
|
|
|
|
if (pOrgMdev)
|
|
{
|
|
if ( (status == GRE_DISP_CHANGE_SUCCESSFUL) ||
|
|
(status == GRE_DISP_CHANGE_NO_CHANGE) )
|
|
{
|
|
//
|
|
// Enable new MDEV.
|
|
//
|
|
|
|
DrvEnableMDEV(pmdev, FALSE);
|
|
|
|
//
|
|
// ISSUE: Reset the state of the DISPLAY_DEVICE_ flags when we
|
|
// destroy the pdev on the device.
|
|
//
|
|
|
|
if (status == GRE_DISP_CHANGE_SUCCESSFUL)
|
|
{
|
|
//
|
|
// When we are success to do mode change,
|
|
// In multi monitor system, it is possible
|
|
// to happen pOrgMdev still contains "enabled"
|
|
// device. Because ,for example, system has 2
|
|
// monitors attached, but only 1 monitor enabled,
|
|
// other one is disabled. so User decided to enable
|
|
// one and disable 'currently enabled' monitor.
|
|
// In this case, we do '1 -> 1' mode change, since
|
|
// there is only 1 monitor before and after mode change.
|
|
// and CreateHDEV() only disable display when it is
|
|
// same pGraphicsDevice. thus this case, old display
|
|
// will be disabled.
|
|
// So here, we scan hdev in Old MDEV, and if there is
|
|
// 'enabled' device which is not used in New MDEV, we
|
|
// disable it.
|
|
//
|
|
|
|
for (i = 0; i < pOrgMdev->chdev; i++)
|
|
{
|
|
PDEV *pdevOld = (PDEV *) pOrgMdev->Dev[i].hdev;
|
|
|
|
//
|
|
// Dualview has already been cleared and disabled
|
|
//
|
|
if ((pdevOld->pGraphicsDevice->stateFlags & DISPLAY_DEVICE_DUALVIEW) &&
|
|
gbInvalidateDualView)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for (j = 0; j < pmdev->chdev; j++)
|
|
{
|
|
PDEV *pdevNew = (PDEV *) pmdev->Dev[j].hdev;
|
|
|
|
//
|
|
// Don't disable this old pdev \ device if we
|
|
// are still using it
|
|
//
|
|
|
|
if ((pdevOld->pGraphicsDevice ==
|
|
pdevNew->pGraphicsDevice)
|
|
||
|
|
(pdevOld->pGraphicsDevice->pVgaDevice &&
|
|
pdevNew->pGraphicsDevice->pVgaDevice))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (j == pmdev->chdev)
|
|
{
|
|
DrvDisableDisplay(pOrgMdev->Dev[i].hdev, TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get rid of old MDEV.
|
|
//
|
|
|
|
DrvDestroyMDEV(pOrgMdev);
|
|
}
|
|
}
|
|
mcip.vDone();
|
|
GreReleaseSemaphoreEx(ghsemShareDevLock);
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvChangeDisplaySettings - Mode Switch status %d\n", status));
|
|
}
|
|
|
|
if (pdevmodeInformation)
|
|
{
|
|
VFREEMEM(pdevmodeInformation);
|
|
}
|
|
|
|
TRACE_INIT(("Drv_Trace: DrvChangeDisplaySettings - Leave - status = %d\n", status));
|
|
|
|
gbInvalidateDualView = FALSE;
|
|
|
|
return status;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrvSetVideoParameters
|
|
*
|
|
* Routine used to pass video parameters structure down to video miniports.
|
|
*
|
|
* ericks Created
|
|
\***************************************************************************/
|
|
|
|
LONG
|
|
DrvSetVideoParameters(
|
|
PUNICODE_STRING pstrDeviceName,
|
|
HDEV hdevPrimary,
|
|
MODE PreviousMode,
|
|
PVOID lParam
|
|
)
|
|
{
|
|
NTSTATUS status = GRE_DISP_CHANGE_BADPARAM;
|
|
PGRAPHICS_DEVICE PhysDisp = NULL;
|
|
|
|
if (pstrDeviceName)
|
|
{
|
|
PhysDisp = DrvGetDeviceFromName(pstrDeviceName, PreviousMode);
|
|
|
|
if (PhysDisp == NULL)
|
|
{
|
|
TRACE_INIT(("Drv_Trace: DrvSetVideoParameters - Leave - Bad Device\n"));
|
|
return GRE_DISP_CHANGE_BADPARAM;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PDEVOBJ pdo(hdevPrimary);
|
|
|
|
if (pdo.bValid())
|
|
{
|
|
PhysDisp = pdo.ppdev->pGraphicsDevice;
|
|
}
|
|
|
|
if (PhysDisp == NULL)
|
|
{
|
|
TRACE_INIT(("Drv_Trace: DrvSetVideoParameters - Leave - Bad Device\n"));
|
|
return GRE_DISP_CHANGE_BADPARAM;
|
|
}
|
|
}
|
|
|
|
if (PhysDisp == (PGRAPHICS_DEVICE) DDML_DRIVER)
|
|
{
|
|
ASSERTGDI(FALSE, "Trying to change TV settings for DDML layer\n");
|
|
return GRE_DISP_CHANGE_BADPARAM;
|
|
}
|
|
|
|
if (PhysDisp != NULL) {
|
|
|
|
ULONG bytesReturned;
|
|
PVIDEOPARAMETERS CapturedData;
|
|
|
|
CapturedData = (PVIDEOPARAMETERS) PALLOCNOZ(2 * sizeof(VIDEOPARAMETERS),
|
|
GDITAG_DRVSUP);
|
|
|
|
if (CapturedData == NULL) {
|
|
return GRE_DISP_CHANGE_FAILED;
|
|
}
|
|
|
|
//
|
|
// Make sure lParam is valid!
|
|
//
|
|
|
|
__try {
|
|
ProbeForRead(lParam, sizeof(VIDEOPARAMETERS), sizeof(UCHAR));
|
|
memcpy(CapturedData, lParam, sizeof(VIDEOPARAMETERS));
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
VFREEMEM(CapturedData);
|
|
return GRE_DISP_CHANGE_BADPARAM;
|
|
}
|
|
|
|
status = GreDeviceIoControl(PhysDisp->pDeviceHandle,
|
|
IOCTL_VIDEO_HANDLE_VIDEOPARAMETERS,
|
|
CapturedData,
|
|
sizeof(VIDEOPARAMETERS),
|
|
CapturedData,
|
|
sizeof(VIDEOPARAMETERS),
|
|
&bytesReturned);
|
|
|
|
|
|
if (status != NO_ERROR) {
|
|
status = GRE_DISP_CHANGE_BADPARAM;
|
|
} else {
|
|
ASSERTGDI(bytesReturned == sizeof(VIDEOPARAMETERS),
|
|
"DrvSetVideoParameters: Not enough data returned\n");
|
|
}
|
|
|
|
__try {
|
|
memcpy(lParam, CapturedData, sizeof(VIDEOPARAMETERS));
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
status = GRE_DISP_CHANGE_BADPARAM;
|
|
}
|
|
|
|
VFREEMEM(CapturedData);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* NtGdiGetMonitorID
|
|
*
|
|
* Return Monitor ID for the given HDC.
|
|
*
|
|
* 14-Dec-1998 hideyukn Created
|
|
\***************************************************************************/
|
|
|
|
BOOL NtGdiGetMonitorID
|
|
(
|
|
HDC hdc,
|
|
DWORD dwSize,
|
|
LPWSTR pszMonitorID
|
|
)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
NTSTATUS Status;
|
|
|
|
DISPLAY_DEVICEW DisplayDevice;
|
|
|
|
//
|
|
// We don't want the session to switch from local to remote or remote to local
|
|
// while we are enumerating display device. Device lock won't prevent this from
|
|
// hapening so we need to grab the User session switch lock.
|
|
//
|
|
|
|
Status = UserSessionSwitchEnterCrit();
|
|
if (Status != STATUS_SUCCESS) {
|
|
return FALSE;
|
|
}
|
|
|
|
XDCOBJ dco(hdc);
|
|
|
|
|
|
if (dco.bValid())
|
|
{
|
|
PDEVOBJ pdo(dco.hdev());
|
|
DEVLOCKOBJ dlo(pdo);
|
|
|
|
if (dlo.bValid())
|
|
{
|
|
PGRAPHICS_DEVICE pGraphicsDevice = NULL;
|
|
|
|
if (pdo.bMetaDriver())
|
|
{
|
|
//
|
|
// If this is meta driver, use primary.
|
|
//
|
|
PVDEV pvdev = (PVDEV) pdo.dhpdev();
|
|
|
|
pGraphicsDevice = ((PPDEV)(pvdev->hdevPrimary))->pGraphicsDevice;
|
|
}
|
|
else
|
|
{
|
|
pGraphicsDevice = ((PPDEV)pdo.hdev())->pGraphicsDevice;
|
|
}
|
|
|
|
if (pGraphicsDevice)
|
|
{
|
|
UNICODE_STRING DeviceName;
|
|
NTSTATUS NtStatus;
|
|
|
|
RtlInitUnicodeString(&DeviceName, pGraphicsDevice->szWinDeviceName);
|
|
|
|
DisplayDevice.cb = sizeof(DisplayDevice);
|
|
|
|
//
|
|
// Get monitor data attached to this display device.
|
|
//
|
|
NtStatus = DrvEnumDisplayDevices(&DeviceName,NULL,0,&DisplayDevice,NULL,KernelMode);
|
|
|
|
if (NT_SUCCESS(NtStatus))
|
|
{
|
|
bRet = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
dco.vUnlockFast();
|
|
}
|
|
|
|
if (bRet)
|
|
{
|
|
DWORD dwLenToBeCopied = (wcslen(DisplayDevice.DeviceID) + 1) * sizeof(WCHAR);
|
|
|
|
if (dwLenToBeCopied <= dwSize)
|
|
{
|
|
__try
|
|
{
|
|
ProbeForWrite(pszMonitorID, dwSize, sizeof(BYTE));
|
|
RtlCopyMemory(pszMonitorID,DisplayDevice.DeviceID,dwLenToBeCopied);
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
WARNINGX(64);
|
|
bRet = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bRet = FALSE;
|
|
}
|
|
}
|
|
//
|
|
// Release the session switch lock.
|
|
//
|
|
|
|
UserSessionSwitchLeaveCrit();
|
|
return (bRet);
|
|
}
|
|
|
|
#ifdef _HYDRA_
|
|
|
|
//
|
|
//!!! Currently, the graphics device list (see drvsup.cxx) is allocated
|
|
//!!! per-Hydra session. AndreVa has proposed that they be allocated
|
|
//!!! globally. He's probably right, but until this changes we need to
|
|
//!!! clean them up during Hydra shutdown.
|
|
//!!!
|
|
//!!! To enable cleanup of the per-Hydra graphics device lists, define
|
|
//!!! _PER_SESSION_GDEVLIST_ in muclean.hxx.
|
|
//
|
|
|
|
#ifdef _PER_SESSION_GDEVLIST_
|
|
|
|
/******************************Public*Routine******************************\
|
|
* MultiUserDrvCleanupGraphicsDeviceList
|
|
*
|
|
* For MultiUserNtGreCleanup (Hydra) cleanup.
|
|
*
|
|
* Cleanup the graphics device list.
|
|
*
|
|
* History:
|
|
* 21-Feb-1998 -by- Gilman Wong [gilmanw]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
VOID
|
|
DrvCleanupOneGraphicsDevice(PGRAPHICS_DEVICE PhysDispVictim)
|
|
{
|
|
//
|
|
// Delete the graphics device data buffers if they exist.
|
|
//
|
|
if (PhysDispVictim->devmodeInfo)
|
|
VFREEMEM(PhysDispVictim->devmodeInfo);
|
|
|
|
if (PhysDispVictim->devmodeMarks)
|
|
VFREEMEM(PhysDispVictim->devmodeMarks);
|
|
|
|
|
|
if (PhysDispVictim->DeviceDescription)
|
|
VFREEMEM(PhysDispVictim->DeviceDescription);
|
|
|
|
if (PhysDispVictim->DisplayDriverNames)
|
|
VFREEMEM(PhysDispVictim->DisplayDriverNames);
|
|
|
|
if (PhysDispVictim->MonitorDevices)
|
|
VFREEMEM(PhysDispVictim->MonitorDevices);
|
|
|
|
if (PhysDispVictim->pFileObject)
|
|
ObDereferenceObject(PhysDispVictim->pFileObject);
|
|
|
|
VFREEMEM(PhysDispVictim);
|
|
}
|
|
|
|
VOID
|
|
DrvCleanupGraphicsDeviceList(PGRAPHICS_DEVICE pGraphicsDeviceList)
|
|
{
|
|
PGRAPHICS_DEVICE PhysDispVictim;
|
|
PGRAPHICS_DEVICE PhysDispNext;
|
|
|
|
//
|
|
// Run the list and delete resources.
|
|
//
|
|
|
|
for (PhysDispVictim = pGraphicsDeviceList;
|
|
PhysDispVictim != NULL;
|
|
PhysDispVictim = PhysDispNext)
|
|
{
|
|
PhysDispNext = PhysDispVictim->pNextGraphicsDevice;
|
|
|
|
//
|
|
// Does this graphics device have a VGA device chained off of it
|
|
// (and is it not self referential)?
|
|
//
|
|
|
|
if ((PhysDispVictim->pVgaDevice) &&
|
|
(PhysDispVictim->pVgaDevice != PhysDispVictim) &&
|
|
(PhysDispVictim->pVgaDevice != &gFullscreenGraphicsDevice) &&
|
|
(PhysDispVictim->pVgaDevice != gPhysDispVGA))
|
|
{
|
|
DrvCleanupOneGraphicsDevice(PhysDispVictim->pVgaDevice);
|
|
}
|
|
|
|
if ((PhysDispVictim != &gFullscreenGraphicsDevice) &&
|
|
(PhysDispVictim != gPhysDispVGA))
|
|
{
|
|
DrvCleanupOneGraphicsDevice(PhysDispVictim);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
MultiUserDrvCleanupGraphicsDeviceList()
|
|
{
|
|
|
|
// Cleanup local graphics device list
|
|
|
|
DrvCleanupGraphicsDeviceList(gpLocalGraphicsDeviceList);
|
|
gpLocalGraphicsDeviceList = NULL;
|
|
|
|
|
|
// Cleanup remote graphics device list
|
|
|
|
DrvCleanupGraphicsDeviceList(gpRemoteGraphicsDeviceList);
|
|
gpRemoteGraphicsDeviceList = NULL;
|
|
|
|
|
|
gpGraphicsDeviceList = NULL;
|
|
|
|
//
|
|
// If WinStation, cleanup driver name allocated in
|
|
// GreMultiUserInitSession (misc.cxx).
|
|
//
|
|
|
|
if (!G_fConsole)
|
|
{
|
|
//
|
|
// If we never made it to GreMultiUserInitSession then don't
|
|
// call FreePool on a static variable (look in misc.cxx)
|
|
//
|
|
|
|
if (G_DisplayDriverNames && G_RemoteVideoFileObject != NULL)
|
|
{
|
|
GdiFreePool(G_DisplayDriverNames);
|
|
}
|
|
}
|
|
|
|
if (gPhysDispVGA)
|
|
{
|
|
DrvCleanupOneGraphicsDevice(gPhysDispVGA);
|
|
gPhysDispVGA = NULL;
|
|
}
|
|
|
|
if (gFullscreenGraphicsDevice.devmodeInfo)
|
|
{
|
|
VFREEMEM(gFullscreenGraphicsDevice.devmodeInfo);
|
|
gFullscreenGraphicsDevice.devmodeInfo = NULL;
|
|
}
|
|
|
|
if (gFullscreenGraphicsDevice.devmodeMarks)
|
|
{
|
|
VFREEMEM(gFullscreenGraphicsDevice.devmodeMarks);
|
|
gFullscreenGraphicsDevice.devmodeMarks = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
BOOL
|
|
DrvIsProtocolAlreadyKnown(VOID)
|
|
{
|
|
PGRAPHICS_DEVICE PhysDisp = gpGraphicsDeviceList;
|
|
|
|
while (PhysDisp != NULL) {
|
|
if (gProtocolType == PhysDisp->ProtocolType) {
|
|
return TRUE;
|
|
}
|
|
PhysDisp = PhysDisp->pNextGraphicsDevice;
|
|
}
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
#endif
|