mirror of
https://github.com/reactos/reactos.git
synced 2024-11-01 12:26:32 +00:00
994e2f289e
Also implement mode switching from adapter advanced settings.
1108 lines
32 KiB
C
1108 lines
32 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS Display Control Panel
|
|
* FILE: dll/cpl/desk/devsett.c
|
|
* PURPOSE: ReactOS Display Control Panel Shell Extension Support
|
|
*/
|
|
|
|
#include "desk.h"
|
|
|
|
#include <cfgmgr32.h>
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
#define DEBUG_DEVSETTINGS
|
|
|
|
typedef struct _CDevSettings
|
|
{
|
|
const struct IDataObjectVtbl *lpIDataObjectVtbl;
|
|
DWORD ref;
|
|
|
|
CLIPFORMAT cfExtInterface; /* "Desk.cpl extension interface" */
|
|
CLIPFORMAT cfDisplayDevice; /* "Display Device" */
|
|
CLIPFORMAT cfDisplayName; /* "Display Name" */
|
|
CLIPFORMAT cfDisplayId; /* "Display ID" */
|
|
CLIPFORMAT cfMonitorName; /* "Monitor Name" */
|
|
CLIPFORMAT cfMonitorDevice; /* "Monitor Device" */
|
|
CLIPFORMAT cfDisplayKey; /* "Display Key" */
|
|
CLIPFORMAT cfDisplayStateFlags; /* "Display State Flags" */
|
|
CLIPFORMAT cfPruningMode; /* "Pruning Mode" */
|
|
|
|
PWSTR pDisplayDevice;
|
|
PWSTR pDisplayName;
|
|
PWSTR pDisplayKey;
|
|
PWSTR pDisplayId;
|
|
PWSTR pMonitorName;
|
|
PWSTR pMonitorDevice;
|
|
|
|
DESK_EXT_INTERFACE ExtInterface;
|
|
|
|
PDEVMODEW lpOrigDevMode;
|
|
PDEVMODEW lpCurDevMode;
|
|
PDEVMODEW * DevModes;
|
|
DWORD nDevModes;
|
|
DWORD StateFlags;
|
|
|
|
union
|
|
{
|
|
DWORD Flags;
|
|
struct
|
|
{
|
|
DWORD bModesPruned : 1;
|
|
DWORD bKeyIsReadOnly : 1;
|
|
DWORD bPruningOn : 1;
|
|
};
|
|
};
|
|
} CDevSettings, *PCDevSettings;
|
|
|
|
#define impl_to_interface(impl,iface) (struct iface *)(&(impl)->lp##iface##Vtbl)
|
|
|
|
static __inline PCDevSettings
|
|
impl_from_IDataObject(struct IDataObject *iface)
|
|
{
|
|
return (PCDevSettings)((ULONG_PTR)iface - FIELD_OFFSET(CDevSettings,
|
|
lpIDataObjectVtbl));
|
|
}
|
|
|
|
static __inline VOID
|
|
pCDevSettings_FreeString(PWCHAR *psz)
|
|
{
|
|
if (*psz != NULL)
|
|
{
|
|
LocalFree((HLOCAL)*psz);
|
|
*psz = NULL;
|
|
}
|
|
}
|
|
|
|
static PWSTR
|
|
pCDevSettings_AllocAndCopyString(const TCHAR *pszSrc)
|
|
{
|
|
SIZE_T c;
|
|
PWSTR str;
|
|
|
|
c = _tcslen(pszSrc) + 1;
|
|
str = (PWSTR)LocalAlloc(LMEM_FIXED,
|
|
c * sizeof(WCHAR));
|
|
if (str != NULL)
|
|
{
|
|
#ifdef UNICODE
|
|
StringCbCopyW(str, c * sizeof(WCHAR),
|
|
pszSrc);
|
|
#else
|
|
MultiByteToWideChar(CP_ACP,
|
|
0,
|
|
pszSrc,
|
|
-1,
|
|
str,
|
|
c);
|
|
#endif
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
static PWSTR
|
|
pCDevSettings_GetMonitorName(const WCHAR *pszDisplayDevice)
|
|
{
|
|
DISPLAY_DEVICEW dd, dd2;
|
|
PWSTR str = NULL;
|
|
|
|
dd.cb = sizeof(dd);
|
|
if (EnumDisplayDevicesW(pszDisplayDevice,
|
|
0,
|
|
&dd,
|
|
0))
|
|
{
|
|
dd2.cb = sizeof(dd2);
|
|
if (EnumDisplayDevicesW(pszDisplayDevice,
|
|
1,
|
|
&dd2,
|
|
0))
|
|
{
|
|
/* There's more than one monitor connected... */
|
|
LoadStringW(hApplet,
|
|
IDS_MULTIPLEMONITORS,
|
|
dd.DeviceString,
|
|
sizeof(dd.DeviceString) / sizeof(dd.DeviceString[0]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* We can't enumerate a monitor, make sure this fact is reported
|
|
to the user! */
|
|
LoadStringW(hApplet,
|
|
IDS_UNKNOWNMONITOR,
|
|
dd.DeviceString,
|
|
sizeof(dd.DeviceString) / sizeof(dd.DeviceString[0]));
|
|
}
|
|
|
|
str = LocalAlloc(LMEM_FIXED,
|
|
(wcslen(dd.DeviceString) + 1) * sizeof(WCHAR));
|
|
if (str != NULL)
|
|
{
|
|
wcscpy(str,
|
|
dd.DeviceString);
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
static PWSTR
|
|
pCDevSettings_GetMonitorDevice(const WCHAR *pszDisplayDevice)
|
|
{
|
|
DISPLAY_DEVICEW dd;
|
|
PWSTR str = NULL;
|
|
|
|
dd.cb = sizeof(dd);
|
|
if (EnumDisplayDevicesW(pszDisplayDevice,
|
|
0,
|
|
&dd,
|
|
0))
|
|
{
|
|
str = LocalAlloc(LMEM_FIXED,
|
|
(wcslen(dd.DeviceName) + 1) * sizeof(WCHAR));
|
|
if (str != NULL)
|
|
{
|
|
wcscpy(str,
|
|
dd.DeviceName);
|
|
}
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
/**
|
|
* @brief
|
|
* Converts a Hardware ID (DeviceID from EnumDisplayDevices)
|
|
* to an unique Device Instance ID.
|
|
*
|
|
* @param[in] pszDevice
|
|
* A pointer to a null-terminated Unicode string
|
|
* containing a Hardware ID.
|
|
* e.g. "PCI\VEN_80EE&DEV_BEEF&SUBSYS_00000000&REV_00"
|
|
*
|
|
* @return
|
|
* A pointer to a null-terminated Unicode string
|
|
* containing an unique Device Instance ID
|
|
* or NULL in case of error.
|
|
* e.g. "PCI\VEN_80EE&DEV_BEEF&SUBSYS_00000000&REV_00\3&267A616A&0&10"
|
|
*
|
|
* @remarks
|
|
* The caller must free the returned string with LocalFree.
|
|
*/
|
|
static PWSTR
|
|
pCDevSettings_GetDeviceInstanceId(const WCHAR *pszDevice)
|
|
{
|
|
HDEVINFO DevInfo;
|
|
SP_DEVINFO_DATA InfoData;
|
|
ULONG BufLen;
|
|
LPWSTR lpDevInstId = NULL;
|
|
|
|
DPRINT("CDevSettings::GetDeviceInstanceId(%ws)!\n", pszDevice);
|
|
|
|
DevInfo = SetupDiGetClassDevsW(NULL, pszDevice, NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT);
|
|
if (DevInfo == INVALID_HANDLE_VALUE)
|
|
{
|
|
DPRINT1("SetupDiGetClassDevsW(\"%ws\") failed: %d\n", pszDevice, GetLastError());
|
|
return NULL;
|
|
}
|
|
|
|
ZeroMemory(&InfoData, sizeof(InfoData));
|
|
InfoData.cbSize = sizeof(InfoData);
|
|
|
|
/* Try to enumerate the first matching device */
|
|
if (!SetupDiEnumDeviceInfo(DevInfo, 0, &InfoData))
|
|
{
|
|
DPRINT1("SetupDiEnumDeviceInfo failed: %d\n", GetLastError());
|
|
return NULL;
|
|
}
|
|
|
|
if (SetupDiGetDeviceInstanceId(DevInfo, &InfoData, NULL, 0, &BufLen) ||
|
|
GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
DPRINT1("SetupDiGetDeviceInstanceId failed: %d\n", GetLastError());
|
|
return NULL;
|
|
}
|
|
|
|
lpDevInstId = LocalAlloc(LMEM_FIXED,
|
|
(BufLen + 1) * sizeof(WCHAR));
|
|
|
|
if (lpDevInstId == NULL)
|
|
{
|
|
DPRINT1("LocalAlloc failed\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (!SetupDiGetDeviceInstanceId(DevInfo, &InfoData, lpDevInstId, BufLen, NULL))
|
|
{
|
|
DPRINT1("SetupDiGetDeviceInstanceId failed: %d\n", GetLastError());
|
|
LocalFree((HLOCAL)lpDevInstId);
|
|
lpDevInstId = NULL;
|
|
}
|
|
else
|
|
{
|
|
DPRINT("instance id: %ws\n", lpDevInstId);
|
|
}
|
|
|
|
return lpDevInstId;
|
|
}
|
|
|
|
|
|
static HKEY
|
|
pCDevSettings_OpenDeviceKey(PCDevSettings This,
|
|
BOOL ReadOnly)
|
|
{
|
|
static const WCHAR szRegPrefix[] = L"\\Registry\\Machine\\";
|
|
PWSTR lpRegKey;
|
|
REGSAM Access = KEY_READ;
|
|
HKEY hKey;
|
|
|
|
lpRegKey = This->pDisplayKey;
|
|
if (lpRegKey != NULL)
|
|
{
|
|
if (wcslen(lpRegKey) >= wcslen(szRegPrefix) &&
|
|
!_wcsnicmp(lpRegKey,
|
|
szRegPrefix,
|
|
wcslen(szRegPrefix)))
|
|
{
|
|
lpRegKey += wcslen(szRegPrefix);
|
|
}
|
|
|
|
if (!ReadOnly)
|
|
Access |= KEY_WRITE;
|
|
|
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
lpRegKey,
|
|
0,
|
|
Access,
|
|
&hKey) == ERROR_SUCCESS)
|
|
{
|
|
return hKey;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
PDEVMODEW DESK_EXT_CALLBACK
|
|
CDevSettings_EnumAllModes(PVOID Context,
|
|
DWORD Index)
|
|
{
|
|
PCDevSettings This = impl_from_IDataObject((IDataObject *)Context);
|
|
DWORD i, idx = 0;
|
|
|
|
DPRINT1("CDevSettings::EnumAllModes(%u)\n", Index);
|
|
|
|
if (!This->DevModes)
|
|
return NULL;
|
|
|
|
for (i = 0; i < This->nDevModes; i++)
|
|
{
|
|
/* FIXME: Add more sanity checks */
|
|
if (!This->DevModes[i])
|
|
continue;
|
|
|
|
if (idx == Index)
|
|
return This->DevModes[i];
|
|
|
|
idx++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
PDEVMODEW DESK_EXT_CALLBACK
|
|
CDevSettings_GetCurrentMode(PVOID Context)
|
|
{
|
|
PCDevSettings This = impl_from_IDataObject((IDataObject *)Context);
|
|
|
|
DPRINT1("CDevSettings::GetCurrentMode\n");
|
|
return This->lpCurDevMode;
|
|
}
|
|
|
|
BOOL DESK_EXT_CALLBACK
|
|
CDevSettings_SetCurrentMode(PVOID Context,
|
|
DEVMODEW *pDevMode)
|
|
{
|
|
PCDevSettings This = impl_from_IDataObject((IDataObject *)Context);
|
|
DWORD i;
|
|
|
|
DPRINT1("CDevSettings::SetCurrentMode(0x%p)\n", pDevMode);
|
|
|
|
if (!This->DevModes)
|
|
return FALSE;
|
|
|
|
for (i = 0; i < This->nDevModes; i++)
|
|
{
|
|
/* FIXME: Add more sanity checks */
|
|
if (!This->DevModes[i])
|
|
continue;
|
|
|
|
if (This->DevModes[i] == pDevMode)
|
|
{
|
|
This->lpCurDevMode = pDevMode;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID DESK_EXT_CALLBACK
|
|
CDevSettings_GetPruningMode(PVOID Context,
|
|
PBOOL pbModesPruned,
|
|
PBOOL pbKeyIsReadOnly,
|
|
PBOOL pbPruningOn)
|
|
{
|
|
PCDevSettings This = impl_from_IDataObject((IDataObject *)Context);
|
|
|
|
DPRINT1("CDevSettings::GetPruningMode(%p,%p,%p)\n", pbModesPruned, pbKeyIsReadOnly, pbPruningOn);
|
|
|
|
*pbModesPruned = This->bModesPruned;
|
|
*pbKeyIsReadOnly = This->bKeyIsReadOnly;
|
|
*pbPruningOn = This->bPruningOn;
|
|
}
|
|
|
|
VOID DESK_EXT_CALLBACK
|
|
CDevSettings_SetPruningMode(PVOID Context,
|
|
BOOL PruningOn)
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwValue;
|
|
PCDevSettings This = impl_from_IDataObject((IDataObject *)Context);
|
|
|
|
DPRINT1("CDevSettings::SetPruningMode(%d)\n", PruningOn);
|
|
|
|
if (This->bModesPruned && !This->bKeyIsReadOnly &&
|
|
PruningOn != This->bPruningOn)
|
|
{
|
|
This->bPruningOn = (PruningOn != FALSE);
|
|
|
|
hKey = pCDevSettings_OpenDeviceKey(This,
|
|
FALSE);
|
|
if (hKey != NULL)
|
|
{
|
|
dwValue = (DWORD)This->bPruningOn;
|
|
|
|
RegSetValueEx(hKey,
|
|
TEXT("PruningMode"),
|
|
0,
|
|
REG_DWORD,
|
|
(const BYTE *)&dwValue,
|
|
sizeof(dwValue));
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
}
|
|
|
|
static VOID
|
|
pCDevSettings_ReadHardwareInfo(HKEY hKey,
|
|
LPCTSTR lpValueName,
|
|
LPWSTR lpBuffer)
|
|
{
|
|
DWORD type = REG_BINARY;
|
|
DWORD size = 128 * sizeof(WCHAR);
|
|
RegQueryValueEx(hKey,
|
|
lpValueName,
|
|
NULL,
|
|
&type,
|
|
(PBYTE)lpBuffer,
|
|
&size);
|
|
}
|
|
|
|
static VOID
|
|
pCDevSettings_InitializeExtInterface(PCDevSettings This)
|
|
{
|
|
PDESK_EXT_INTERFACE Interface = &This->ExtInterface;
|
|
HKEY hKeyDev;
|
|
|
|
ZeroMemory(Interface,
|
|
sizeof(*Interface));
|
|
Interface->cbSize = sizeof(*Interface);
|
|
|
|
/* Initialize the callback table */
|
|
Interface->Context = impl_to_interface(This, IDataObject);
|
|
Interface->EnumAllModes = CDevSettings_EnumAllModes;
|
|
Interface->SetCurrentMode = CDevSettings_SetCurrentMode;
|
|
Interface->GetCurrentMode = CDevSettings_GetCurrentMode;
|
|
Interface->SetPruningMode = CDevSettings_SetPruningMode;
|
|
Interface->GetPruningMode = CDevSettings_GetPruningMode;
|
|
|
|
/* Read the HardwareInformation.* values from the registry key */
|
|
hKeyDev = pCDevSettings_OpenDeviceKey(This,
|
|
TRUE);
|
|
if (hKeyDev != NULL)
|
|
{
|
|
DWORD dwType, dwMemSize = 0;
|
|
DWORD dwSize = sizeof(dwMemSize);
|
|
|
|
if (RegQueryValueEx(hKeyDev,
|
|
TEXT("HardwareInformation.MemorySize"),
|
|
NULL,
|
|
&dwType,
|
|
(PBYTE)&dwMemSize,
|
|
&dwSize) == ERROR_SUCCESS &&
|
|
(dwType == REG_BINARY || dwType == REG_DWORD) &&
|
|
dwSize == sizeof(dwMemSize))
|
|
{
|
|
dwMemSize /= 1024;
|
|
|
|
if (dwMemSize > 1024)
|
|
{
|
|
dwMemSize /= 1024;
|
|
if (dwMemSize > 1024)
|
|
{
|
|
wsprintf(Interface->MemorySize,
|
|
_T("%u GB"),
|
|
dwMemSize / 1024);
|
|
}
|
|
else
|
|
{
|
|
wsprintf(Interface->MemorySize,
|
|
_T("%u MB"),
|
|
dwMemSize);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wsprintf(Interface->MemorySize,
|
|
_T("%u KB"),
|
|
dwMemSize);
|
|
}
|
|
}
|
|
|
|
pCDevSettings_ReadHardwareInfo(hKeyDev,
|
|
TEXT("HardwareInformation.ChipType"),
|
|
Interface->ChipType);
|
|
pCDevSettings_ReadHardwareInfo(hKeyDev,
|
|
TEXT("HardwareInformation.DacType"),
|
|
Interface->DacType);
|
|
pCDevSettings_ReadHardwareInfo(hKeyDev,
|
|
TEXT("HardwareInformation.AdapterString"),
|
|
Interface->AdapterString);
|
|
pCDevSettings_ReadHardwareInfo(hKeyDev,
|
|
TEXT("HardwareInformation.BiosString"),
|
|
Interface->BiosString);
|
|
RegCloseKey(hKeyDev);
|
|
}
|
|
}
|
|
|
|
static HRESULT
|
|
pCDevSettings_Initialize(PCDevSettings This,
|
|
PDISPLAY_DEVICE_ENTRY DisplayDeviceInfo)
|
|
{
|
|
HKEY hKey;
|
|
DWORD i = 0, dwSize;
|
|
DEVMODEW devmode;
|
|
|
|
This->lpOrigDevMode = NULL;
|
|
This->lpCurDevMode = NULL;
|
|
This->DevModes = NULL;
|
|
This->nDevModes = 0;
|
|
This->Flags = 0;
|
|
This->StateFlags = DisplayDeviceInfo->DeviceStateFlags;
|
|
DPRINT1("This->StateFlags: %x\n", This->StateFlags);
|
|
|
|
/* Register clipboard formats */
|
|
This->cfExtInterface = RegisterClipboardFormat(DESK_EXT_EXTINTERFACE);
|
|
This->cfDisplayDevice = RegisterClipboardFormat(DESK_EXT_DISPLAYDEVICE);
|
|
This->cfDisplayName = RegisterClipboardFormat(DESK_EXT_DISPLAYNAME);
|
|
This->cfDisplayId = RegisterClipboardFormat(DESK_EXT_DISPLAYID);
|
|
This->cfDisplayKey = RegisterClipboardFormat(DESK_EXT_DISPLAYKEY);
|
|
This->cfDisplayStateFlags = RegisterClipboardFormat(DESK_EXT_DISPLAYSTATEFLAGS);
|
|
This->cfMonitorName = RegisterClipboardFormat(DESK_EXT_MONITORNAME);
|
|
This->cfMonitorDevice = RegisterClipboardFormat(DESK_EXT_MONITORDEVICE);
|
|
This->cfPruningMode = RegisterClipboardFormat(DESK_EXT_PRUNINGMODE);
|
|
|
|
/* Copy the device name */
|
|
This->pDisplayDevice = pCDevSettings_AllocAndCopyString(DisplayDeviceInfo->DeviceName);
|
|
DPRINT1("This->pDisplayDevice: %ws\n", This->pDisplayDevice);
|
|
This->pDisplayName = pCDevSettings_AllocAndCopyString(DisplayDeviceInfo->DeviceDescription);
|
|
DPRINT1("This->pDisplayName: %ws\n", This->pDisplayName);
|
|
This->pDisplayKey = pCDevSettings_AllocAndCopyString(DisplayDeviceInfo->DeviceKey);
|
|
DPRINT1("This->pDisplayKey: %ws\n", This->pDisplayKey);
|
|
This->pDisplayId = pCDevSettings_GetDeviceInstanceId(DisplayDeviceInfo->DeviceID);
|
|
DPRINT1("This->pDisplayId: %ws\n", This->pDisplayId);
|
|
This->pMonitorName = pCDevSettings_GetMonitorName(This->pDisplayDevice);
|
|
DPRINT1("This->pMonitorName: %ws\n", This->pMonitorName);
|
|
This->pMonitorDevice = pCDevSettings_GetMonitorDevice(This->pDisplayDevice);
|
|
DPRINT1("This->pMonitorDevice: %ws\n", This->pMonitorDevice);
|
|
|
|
/* Check pruning mode */
|
|
This->bModesPruned = ((DisplayDeviceInfo->DeviceStateFlags & DISPLAY_DEVICE_MODESPRUNED) != 0);
|
|
hKey = pCDevSettings_OpenDeviceKey(This,
|
|
FALSE);
|
|
if (hKey == NULL)
|
|
{
|
|
hKey = pCDevSettings_OpenDeviceKey(This,
|
|
FALSE);
|
|
This->bKeyIsReadOnly = TRUE;
|
|
}
|
|
if (hKey != NULL)
|
|
{
|
|
DWORD dw = 0;
|
|
DWORD dwType;
|
|
|
|
dwSize = sizeof(dw);
|
|
if (RegQueryValueEx(hKey,
|
|
TEXT("PruningMode"),
|
|
NULL,
|
|
&dwType,
|
|
(PBYTE)&dw,
|
|
&dwSize) == ERROR_SUCCESS)
|
|
{
|
|
if (dwType == REG_DWORD && dwSize == sizeof(dw))
|
|
This->bPruningOn = (dw != 0);
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
/* Initialize display modes */
|
|
ZeroMemory(&devmode, sizeof(devmode));
|
|
devmode.dmSize = (WORD)sizeof(devmode);
|
|
while (EnumDisplaySettingsExW(This->pDisplayDevice, i, &devmode, EDS_RAWMODE))
|
|
{
|
|
dwSize = devmode.dmSize + devmode.dmDriverExtra;
|
|
PDEVMODEW pDevMode = LocalAlloc(LMEM_FIXED, dwSize);
|
|
PDEVMODEW * DevModesNew = NULL;
|
|
|
|
if (pDevMode)
|
|
{
|
|
CopyMemory(pDevMode,
|
|
&devmode,
|
|
dwSize);
|
|
|
|
dwSize = (This->nDevModes + 1) * sizeof(pDevMode);
|
|
DevModesNew = LocalAlloc(LMEM_FIXED, dwSize);
|
|
if (DevModesNew)
|
|
{
|
|
if (This->DevModes)
|
|
{
|
|
CopyMemory(DevModesNew,
|
|
This->DevModes,
|
|
This->nDevModes * sizeof(pDevMode));
|
|
|
|
LocalFree(This->DevModes);
|
|
}
|
|
|
|
This->DevModes = DevModesNew;
|
|
This->DevModes[This->nDevModes++] = pDevMode;
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("LocalAlloc failed to allocate %d bytes\n", dwSize);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("LocalAlloc failed to allocate %d bytes\n", dwSize);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
devmode.dmDriverExtra = 0;
|
|
i++;
|
|
}
|
|
|
|
/* FIXME: Detect duplicated modes and mark them.
|
|
* Enumeration functions should check these marks
|
|
* and skip corresponding array entries. */
|
|
|
|
/* Get current display mode */
|
|
ZeroMemory(&devmode, sizeof(devmode));
|
|
devmode.dmSize = (WORD)sizeof(devmode);
|
|
if (EnumDisplaySettingsExW(This->pDisplayDevice, ENUM_CURRENT_SETTINGS, &devmode, 0))
|
|
{
|
|
for (i = 0; i < This->nDevModes; i++)
|
|
{
|
|
PDEVMODEW CurMode = This->DevModes[i];
|
|
|
|
if (!CurMode)
|
|
continue;
|
|
|
|
if (((CurMode->dmFields & DM_PELSWIDTH) && devmode.dmPelsWidth == CurMode->dmPelsWidth) &&
|
|
((CurMode->dmFields & DM_PELSHEIGHT) && devmode.dmPelsHeight == CurMode->dmPelsHeight) &&
|
|
((CurMode->dmFields & DM_BITSPERPEL) && devmode.dmBitsPerPel == CurMode->dmBitsPerPel) &&
|
|
((CurMode->dmFields & DM_DISPLAYFREQUENCY) && devmode.dmDisplayFrequency == CurMode->dmDisplayFrequency))
|
|
{
|
|
This->lpOrigDevMode = This->lpCurDevMode = CurMode;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Initialize the shell extension interface */
|
|
pCDevSettings_InitializeExtInterface(This);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static VOID
|
|
pCDevSettings_Free(PCDevSettings This)
|
|
{
|
|
This->lpOrigDevMode = NULL;
|
|
This->lpCurDevMode = NULL;
|
|
while (This->nDevModes)
|
|
{
|
|
LocalFree(This->DevModes[--This->nDevModes]);
|
|
}
|
|
LocalFree(This->DevModes);
|
|
This->DevModes = NULL;
|
|
|
|
pCDevSettings_FreeString(&This->pDisplayDevice);
|
|
pCDevSettings_FreeString(&This->pDisplayName);
|
|
pCDevSettings_FreeString(&This->pDisplayKey);
|
|
pCDevSettings_FreeString(&This->pDisplayId);
|
|
pCDevSettings_FreeString(&This->pMonitorName);
|
|
pCDevSettings_FreeString(&This->pMonitorDevice);
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
CDevSettings_QueryInterface(IDataObject* iface,
|
|
REFIID riid,
|
|
void** ppvObject)
|
|
{
|
|
PCDevSettings This = impl_from_IDataObject(iface);
|
|
|
|
*ppvObject = NULL;
|
|
|
|
if (IsEqualGUID(riid,
|
|
&IID_IUnknown) ||
|
|
IsEqualGUID(riid,
|
|
&IID_IDataObject))
|
|
{
|
|
*ppvObject = (PVOID)impl_to_interface(This, IDataObject);
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("CDevSettings::QueryInterface: Queried unknown interface\n");
|
|
}
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static ULONG STDMETHODCALLTYPE
|
|
CDevSettings_AddRef(IDataObject* iface)
|
|
{
|
|
PCDevSettings This = impl_from_IDataObject(iface);
|
|
return (ULONG)InterlockedIncrement((PLONG)&This->ref);
|
|
}
|
|
|
|
static ULONG STDMETHODCALLTYPE
|
|
CDevSettings_Release(IDataObject* iface)
|
|
{
|
|
ULONG refs;
|
|
PCDevSettings This = impl_from_IDataObject(iface);
|
|
refs = (ULONG)InterlockedDecrement((PLONG)&This->ref);
|
|
if (refs == 0)
|
|
pCDevSettings_Free(This);
|
|
|
|
return refs;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
CDevSettings_GetData(IDataObject* iface,
|
|
FORMATETC* pformatetcIn,
|
|
STGMEDIUM* pmedium)
|
|
{
|
|
static const WCHAR szEmpty[] = {0};
|
|
HRESULT hr;
|
|
PCWSTR pszRet = NULL;
|
|
PWSTR pszBuf;
|
|
PCDevSettings This = impl_from_IDataObject(iface);
|
|
|
|
ZeroMemory(pmedium,
|
|
sizeof(STGMEDIUM));
|
|
|
|
hr = IDataObject_QueryGetData(iface,
|
|
pformatetcIn);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
/* Return the requested data back to the shell extension */
|
|
|
|
if (pformatetcIn->cfFormat == This->cfDisplayDevice)
|
|
{
|
|
pszRet = This->pDisplayDevice;
|
|
DPRINT1("CDevSettings::GetData returns display device %ws\n", pszRet);
|
|
}
|
|
else if (pformatetcIn->cfFormat == This->cfDisplayName)
|
|
{
|
|
pszRet = This->pDisplayName;
|
|
DPRINT1("CDevSettings::GetData returns display name %ws\n", pszRet);
|
|
}
|
|
else if (pformatetcIn->cfFormat == This->cfDisplayKey)
|
|
{
|
|
pszRet = This->pDisplayKey;
|
|
DPRINT1("CDevSettings::GetData returns display key %ws\n", pszRet);
|
|
}
|
|
else if (pformatetcIn->cfFormat == This->cfDisplayId)
|
|
{
|
|
pszRet = This->pDisplayId;
|
|
DPRINT1("CDevSettings::GetData returns display id %ws\n", pszRet);
|
|
}
|
|
else if (pformatetcIn->cfFormat == This->cfMonitorName)
|
|
{
|
|
pszRet = This->pMonitorName;
|
|
DPRINT1("CDevSettings::GetData returns monitor name %ws\n", pszRet);
|
|
}
|
|
else if (pformatetcIn->cfFormat == This->cfMonitorDevice)
|
|
{
|
|
pszRet = This->pMonitorDevice;
|
|
DPRINT1("CDevSettings::GetData returns monitor device %ws\n", pszRet);
|
|
}
|
|
else if (pformatetcIn->cfFormat == This->cfExtInterface)
|
|
{
|
|
PDESK_EXT_INTERFACE pIface;
|
|
|
|
pIface = GlobalAlloc(GPTR,
|
|
sizeof(*pIface));
|
|
if (pIface != NULL)
|
|
{
|
|
CopyMemory(pIface,
|
|
&This->ExtInterface,
|
|
sizeof(This->ExtInterface));
|
|
|
|
DPRINT1("CDevSettings::GetData returns the desk.cpl extension interface\n");
|
|
|
|
pmedium->tymed = TYMED_HGLOBAL;
|
|
pmedium->hGlobal = pIface;
|
|
|
|
return S_OK;
|
|
}
|
|
else
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
else if (pformatetcIn->cfFormat == This->cfDisplayStateFlags)
|
|
{
|
|
PDWORD pdw;
|
|
|
|
pdw = GlobalAlloc(GPTR,
|
|
sizeof(*pdw));
|
|
if (pdw != NULL)
|
|
{
|
|
*pdw = This->StateFlags;
|
|
|
|
DPRINT1("CDevSettings::GetData returns the display state flags %x\n", This->StateFlags);
|
|
|
|
pmedium->tymed = TYMED_HGLOBAL;
|
|
pmedium->hGlobal = pdw;
|
|
|
|
return S_OK;
|
|
}
|
|
else
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
else if (pformatetcIn->cfFormat == This->cfPruningMode)
|
|
{
|
|
PBYTE pb;
|
|
|
|
pb = GlobalAlloc(GPTR,
|
|
sizeof(*pb));
|
|
if (pb != NULL)
|
|
{
|
|
*pb = (This->bModesPruned && This->bPruningOn);
|
|
|
|
pmedium->tymed = TYMED_HGLOBAL;
|
|
pmedium->hGlobal = pb;
|
|
|
|
return S_OK;
|
|
}
|
|
else
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
/* NOTE: This only returns null-terminated strings! */
|
|
if (pszRet == NULL)
|
|
pszRet = szEmpty;
|
|
|
|
pszBuf = GlobalAlloc(GPTR,
|
|
(wcslen(pszRet) + 1) * sizeof(WCHAR));
|
|
if (pszBuf != NULL)
|
|
{
|
|
hr = StringCbCopyW(pszBuf, (wcslen(pszRet) + 1) * sizeof(WCHAR), pszRet);
|
|
if (FAILED(hr))
|
|
{
|
|
GlobalFree(pszBuf);
|
|
return hr;
|
|
}
|
|
|
|
pmedium->tymed = TYMED_HGLOBAL;
|
|
pmedium->hGlobal = pszBuf;
|
|
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
CDevSettings_GetDataHere(IDataObject* iface,
|
|
FORMATETC* pformatetc,
|
|
STGMEDIUM* pmedium)
|
|
{
|
|
ZeroMemory(pformatetc,
|
|
sizeof(*pformatetc));
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
CDevSettings_QueryGetData(IDataObject* iface,
|
|
FORMATETC* pformatetc)
|
|
{
|
|
#if DEBUG
|
|
TCHAR szFormatName[255];
|
|
#endif
|
|
PCDevSettings This = impl_from_IDataObject(iface);
|
|
|
|
if (pformatetc->dwAspect != DVASPECT_CONTENT)
|
|
return DV_E_DVASPECT;
|
|
|
|
if (pformatetc->lindex != -1)
|
|
return DV_E_LINDEX;
|
|
|
|
if (!(pformatetc->tymed & TYMED_HGLOBAL))
|
|
return DV_E_TYMED;
|
|
|
|
/* Check if the requested data can be provided */
|
|
if (pformatetc->cfFormat == This->cfExtInterface ||
|
|
pformatetc->cfFormat == This->cfDisplayDevice ||
|
|
pformatetc->cfFormat == This->cfDisplayName ||
|
|
pformatetc->cfFormat == This->cfDisplayId ||
|
|
pformatetc->cfFormat == This->cfDisplayKey ||
|
|
pformatetc->cfFormat == This->cfDisplayStateFlags ||
|
|
pformatetc->cfFormat == This->cfMonitorDevice ||
|
|
pformatetc->cfFormat == This->cfMonitorName ||
|
|
pformatetc->cfFormat == This->cfPruningMode)
|
|
{
|
|
return S_OK;
|
|
}
|
|
#if DEBUG
|
|
else
|
|
{
|
|
if (GetClipboardFormatName(pformatetc->cfFormat,
|
|
szFormatName,
|
|
sizeof(szFormatName) / sizeof(szFormatName[0])))
|
|
{
|
|
DPRINT1("CDevSettings::QueryGetData(\"%ws\")\n", szFormatName);
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("CDevSettings::QueryGetData(Format %u)\n", (unsigned int)pformatetc->cfFormat);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return DV_E_FORMATETC;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
CDevSettings_GetCanonicalFormatEtc(IDataObject* iface,
|
|
FORMATETC* pformatectIn,
|
|
FORMATETC* pformatetcOut)
|
|
{
|
|
HRESULT hr;
|
|
|
|
DPRINT1("CDevSettings::GetCanonicalFormatEtc\n");
|
|
|
|
hr = IDataObject_QueryGetData(iface,
|
|
pformatectIn);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CopyMemory(pformatetcOut,
|
|
pformatectIn,
|
|
sizeof(FORMATETC));
|
|
|
|
/* Make sure the data is target device independent */
|
|
if (pformatectIn->ptd == NULL)
|
|
hr = DATA_S_SAMEFORMATETC;
|
|
else
|
|
{
|
|
pformatetcOut->ptd = NULL;
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ZeroMemory(pformatetcOut,
|
|
sizeof(FORMATETC));
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
CDevSettings_SetData(IDataObject* iface,
|
|
FORMATETC* pformatetc,
|
|
STGMEDIUM* pmedium,
|
|
BOOL fRelease)
|
|
{
|
|
DPRINT1("CDevSettings::SetData UNIMPLEMENTED\n");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static __inline VOID
|
|
pCDevSettings_FillFormatEtc(FORMATETC *pFormatEtc,
|
|
CLIPFORMAT cf)
|
|
{
|
|
pFormatEtc->cfFormat = cf;
|
|
pFormatEtc->ptd = NULL;
|
|
pFormatEtc->dwAspect = DVASPECT_CONTENT;
|
|
pFormatEtc->lindex = -1;
|
|
pFormatEtc->tymed = TYMED_HGLOBAL;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
CDevSettings_EnumFormatEtc(IDataObject* iface,
|
|
DWORD dwDirection,
|
|
IEnumFORMATETC** ppenumFormatEtc)
|
|
{
|
|
HRESULT hr;
|
|
FORMATETC fetc[9];
|
|
PCDevSettings This = impl_from_IDataObject(iface);
|
|
|
|
*ppenumFormatEtc = NULL;
|
|
|
|
if (dwDirection == DATADIR_GET)
|
|
{
|
|
pCDevSettings_FillFormatEtc(&fetc[0],
|
|
This->cfExtInterface);
|
|
pCDevSettings_FillFormatEtc(&fetc[1],
|
|
This->cfDisplayDevice);
|
|
pCDevSettings_FillFormatEtc(&fetc[2],
|
|
This->cfDisplayName);
|
|
pCDevSettings_FillFormatEtc(&fetc[3],
|
|
This->cfDisplayId);
|
|
pCDevSettings_FillFormatEtc(&fetc[4],
|
|
This->cfDisplayKey);
|
|
pCDevSettings_FillFormatEtc(&fetc[5],
|
|
This->cfDisplayStateFlags);
|
|
pCDevSettings_FillFormatEtc(&fetc[6],
|
|
This->cfMonitorName);
|
|
pCDevSettings_FillFormatEtc(&fetc[7],
|
|
This->cfMonitorDevice);
|
|
pCDevSettings_FillFormatEtc(&fetc[8],
|
|
This->cfPruningMode);
|
|
|
|
hr = SHCreateStdEnumFmtEtc(sizeof(fetc) / sizeof(fetc[0]),
|
|
fetc,
|
|
ppenumFormatEtc);
|
|
}
|
|
else
|
|
hr = E_NOTIMPL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
CDevSettings_DAdvise(IDataObject* iface,
|
|
FORMATETC* pformatetc,
|
|
DWORD advf,
|
|
IAdviseSink* pAdvSink,
|
|
DWORD* pdwConnection)
|
|
{
|
|
*pdwConnection = 0;
|
|
return OLE_E_ADVISENOTSUPPORTED;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
CDevSettings_DUnadvise(IDataObject* iface,
|
|
DWORD dwConnection)
|
|
{
|
|
return OLE_E_ADVISENOTSUPPORTED;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
CDevSettings_EnumDAdvise(IDataObject* iface,
|
|
IEnumSTATDATA** ppenumAdvise)
|
|
{
|
|
*ppenumAdvise = NULL;
|
|
return OLE_E_ADVISENOTSUPPORTED;
|
|
}
|
|
|
|
static const struct IDataObjectVtbl vtblIDataObject = {
|
|
CDevSettings_QueryInterface,
|
|
CDevSettings_AddRef,
|
|
CDevSettings_Release,
|
|
CDevSettings_GetData,
|
|
CDevSettings_GetDataHere,
|
|
CDevSettings_QueryGetData,
|
|
CDevSettings_GetCanonicalFormatEtc,
|
|
CDevSettings_SetData,
|
|
CDevSettings_EnumFormatEtc,
|
|
CDevSettings_DAdvise,
|
|
CDevSettings_DUnadvise,
|
|
CDevSettings_EnumDAdvise,
|
|
};
|
|
|
|
IDataObject *
|
|
CreateDevSettings(PDISPLAY_DEVICE_ENTRY DisplayDeviceInfo)
|
|
{
|
|
PCDevSettings This;
|
|
|
|
This = HeapAlloc(GetProcessHeap(),
|
|
0,
|
|
sizeof(*This));
|
|
if (This != NULL)
|
|
{
|
|
This->lpIDataObjectVtbl = &vtblIDataObject;
|
|
This->ref = 1;
|
|
|
|
if (SUCCEEDED(pCDevSettings_Initialize(This,
|
|
DisplayDeviceInfo)))
|
|
{
|
|
return impl_to_interface(This, IDataObject);
|
|
}
|
|
|
|
CDevSettings_Release(impl_to_interface(This, IDataObject));
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
LONG WINAPI
|
|
DisplaySaveSettings(PVOID pContext,
|
|
HWND hwndPropSheet)
|
|
{
|
|
PCDevSettings This = impl_from_IDataObject((IDataObject *)pContext);
|
|
LONG rc = DISP_CHANGE_SUCCESSFUL;
|
|
|
|
if (This->lpCurDevMode != This->lpOrigDevMode)
|
|
{
|
|
SETTINGS_ENTRY seOrig, seCur;
|
|
BOOL Ret;
|
|
|
|
seOrig.dmPelsWidth = This->lpOrigDevMode->dmPelsWidth;
|
|
seOrig.dmPelsHeight = This->lpOrigDevMode->dmPelsHeight;
|
|
seOrig.dmBitsPerPel = This->lpOrigDevMode->dmBitsPerPel;
|
|
seOrig.dmDisplayFrequency = This->lpOrigDevMode->dmDisplayFrequency;
|
|
|
|
seCur.dmPelsWidth = This->lpCurDevMode->dmPelsWidth;
|
|
seCur.dmPelsHeight = This->lpCurDevMode->dmPelsHeight;
|
|
seCur.dmBitsPerPel = This->lpCurDevMode->dmBitsPerPel;
|
|
seCur.dmDisplayFrequency = This->lpCurDevMode->dmDisplayFrequency;
|
|
|
|
Ret = SwitchDisplayMode(hwndPropSheet,
|
|
This->pDisplayDevice,
|
|
&seOrig,
|
|
&seCur,
|
|
&rc);
|
|
|
|
if (rc == DISP_CHANGE_SUCCESSFUL)
|
|
{
|
|
if (Ret)
|
|
This->lpOrigDevMode = This->lpCurDevMode;
|
|
else
|
|
This->lpCurDevMode = This->lpOrigDevMode;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|