mirror of
https://github.com/narzoul/DDrawCompat
synced 2024-12-30 08:55:36 +01:00
391 lines
11 KiB
C++
391 lines
11 KiB
C++
#include <Common/CompatPtr.h>
|
|
#include <Common/CompatRef.h>
|
|
#include <D3dDdi/Device.h>
|
|
#include <D3dDdi/KernelModeThunks.h>
|
|
#include <D3dDdi/Resource.h>
|
|
#include <D3dDdi/SurfaceRepository.h>
|
|
#include <DDraw/DirectDraw.h>
|
|
#include <DDraw/DirectDrawSurface.h>
|
|
#include <DDraw/RealPrimarySurface.h>
|
|
#include <DDraw/Surfaces/PrimarySurface.h>
|
|
#include <DDraw/Surfaces/PrimarySurfaceImpl.h>
|
|
#include <DDraw/Surfaces/TagSurface.h>
|
|
#include <Gdi/Palette.h>
|
|
#include <Gdi/VirtualScreen.h>
|
|
#include <Win32/DisplayMode.h>
|
|
|
|
namespace
|
|
{
|
|
CompatWeakPtr<IDirectDrawSurface7> g_primarySurface;
|
|
CompatWeakPtr<IDirectDrawSurface7> g_gdiPrimarySurface;
|
|
D3dDdi::Device* g_device = nullptr;
|
|
HANDLE g_gdiDriverResource = nullptr;
|
|
HANDLE g_gdiRuntimeResource = nullptr;
|
|
HANDLE g_frontResource = nullptr;
|
|
DWORD g_origCaps = 0;
|
|
HWND g_deviceWindow = nullptr;
|
|
HPALETTE g_palette = nullptr;
|
|
std::wstring g_deviceName;
|
|
RECT g_monitorRect = {};
|
|
|
|
CompatPtr<IDirectDrawSurface7> createGdiPrimarySurface(CompatRef<IDirectDraw> dd)
|
|
{
|
|
LOG_FUNC("PrimarySurface::createGdiPrimarySurface", &dd);
|
|
|
|
auto ddLcl = DDraw::DirectDraw::getInt(dd.get()).lpLcl;
|
|
auto tagSurface = DDraw::TagSurface::get(ddLcl);
|
|
if (!tagSurface)
|
|
{
|
|
return LOG_RESULT(nullptr);
|
|
}
|
|
|
|
auto resource = DDraw::DirectDrawSurface::getDriverResourceHandle(*tagSurface->getDDS());
|
|
if (!resource)
|
|
{
|
|
return LOG_RESULT(nullptr);
|
|
}
|
|
|
|
auto device = D3dDdi::Device::findDeviceByResource(resource);
|
|
if (!device)
|
|
{
|
|
return LOG_RESULT(nullptr);
|
|
}
|
|
|
|
auto& repo = D3dDdi::SurfaceRepository::get(device->getAdapter());
|
|
D3dDdi::SurfaceRepository::Surface surface = {};
|
|
repo.getSurface(surface, 1, 1, D3DDDIFMT_X8R8G8B8, DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY);
|
|
|
|
LOG_RESULT(surface.surface.get());
|
|
return surface.surface;
|
|
}
|
|
}
|
|
|
|
namespace DDraw
|
|
{
|
|
PrimarySurface::~PrimarySurface()
|
|
{
|
|
LOG_FUNC("PrimarySurface::~PrimarySurface");
|
|
|
|
g_device = nullptr;
|
|
g_gdiRuntimeResource = nullptr;
|
|
g_gdiDriverResource = nullptr;
|
|
g_frontResource = nullptr;
|
|
g_primarySurface = nullptr;
|
|
g_gdiPrimarySurface.release();
|
|
g_origCaps = 0;
|
|
g_deviceWindow = nullptr;
|
|
if (g_palette)
|
|
{
|
|
DeleteObject(g_palette);
|
|
g_palette = nullptr;
|
|
}
|
|
g_deviceName.clear();
|
|
g_monitorRect = {};
|
|
s_palette = nullptr;
|
|
|
|
DDraw::RealPrimarySurface::release();
|
|
}
|
|
|
|
template <typename TDirectDraw, typename TSurface, typename TSurfaceDesc>
|
|
HRESULT PrimarySurface::create(CompatRef<TDirectDraw> dd, TSurfaceDesc desc, TSurface*& surface)
|
|
{
|
|
LOG_FUNC("PrimarySurface::create", &dd, desc, surface);
|
|
DDraw::RealPrimarySurface::destroyDefaultPrimary();
|
|
|
|
const auto& dm = DDraw::DirectDraw::getDisplayMode(*CompatPtr<IDirectDraw7>::from(&dd));
|
|
auto deviceName = D3dDdi::KernelModeThunks::getAdapterInfo(*CompatPtr<IDirectDraw7>::from(&dd)).deviceName;
|
|
auto prevMonitorRect = g_monitorRect;
|
|
g_monitorRect = Win32::DisplayMode::getMonitorInfo(deviceName).rcMonitor;
|
|
g_monitorRect.right = g_monitorRect.left + dm.dwWidth;
|
|
g_monitorRect.bottom = g_monitorRect.top + dm.dwHeight;
|
|
|
|
HRESULT result = RealPrimarySurface::create(*CompatPtr<IDirectDraw>::from(&dd));
|
|
if (FAILED(result))
|
|
{
|
|
g_monitorRect = prevMonitorRect;
|
|
return LOG_RESULT(result);
|
|
}
|
|
|
|
const DWORD origCaps = desc.ddsCaps.dwCaps;
|
|
auto privateData(std::make_unique<PrimarySurface>(desc.dwFlags, origCaps));
|
|
auto data = privateData.get();
|
|
|
|
desc.dwFlags |= DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
|
|
desc.dwWidth = dm.dwWidth;
|
|
desc.dwHeight = dm.dwHeight;
|
|
desc.ddsCaps.dwCaps &= ~(DDSCAPS_PRIMARYSURFACE | DDSCAPS_SYSTEMMEMORY |
|
|
DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM | DDSCAPS_NONLOCALVIDMEM);
|
|
desc.ddsCaps.dwCaps |= DDSCAPS_OFFSCREENPLAIN;
|
|
desc.ddpfPixelFormat = dm.ddpfPixelFormat;
|
|
|
|
result = Surface::create(dd, desc, surface, std::move(privateData));
|
|
if (FAILED(result))
|
|
{
|
|
LOG_INFO << "ERROR: Failed to create the compat primary surface: " << Compat::hex(result);
|
|
g_monitorRect = prevMonitorRect;
|
|
RealPrimarySurface::release();
|
|
return LOG_RESULT(result);
|
|
}
|
|
|
|
g_deviceName = deviceName;
|
|
g_origCaps = origCaps;
|
|
g_deviceWindow = *DDraw::DirectDraw::getDeviceWindowPtr(dd.get());
|
|
g_gdiPrimarySurface = createGdiPrimarySurface(*CompatPtr<IDirectDraw>::from(&dd)).detach();
|
|
|
|
if (desc.ddpfPixelFormat.dwRGBBitCount <= 8)
|
|
{
|
|
LOGPALETTE lp = {};
|
|
lp.palVersion = 0x300;
|
|
lp.palNumEntries = 1;
|
|
g_palette = CreatePalette(&lp);
|
|
ResizePalette(g_palette, 256);
|
|
}
|
|
|
|
g_device = D3dDdi::Device::findDeviceByResource(DirectDrawSurface::getDriverResourceHandle(*surface));
|
|
data->restore();
|
|
D3dDdi::Device::updateAllConfig();
|
|
return LOG_RESULT(DD_OK);
|
|
}
|
|
|
|
template HRESULT PrimarySurface::create(
|
|
CompatRef<IDirectDraw> dd, DDSURFACEDESC desc, IDirectDrawSurface*& surface);
|
|
template HRESULT PrimarySurface::create(
|
|
CompatRef<IDirectDraw2> dd, DDSURFACEDESC desc, IDirectDrawSurface*& surface);
|
|
template HRESULT PrimarySurface::create(
|
|
CompatRef<IDirectDraw4> dd, DDSURFACEDESC2 desc, IDirectDrawSurface4*& surface);
|
|
template HRESULT PrimarySurface::create(
|
|
CompatRef<IDirectDraw7> dd, DDSURFACEDESC2 desc, IDirectDrawSurface7*& surface);
|
|
|
|
void PrimarySurface::createImpl()
|
|
{
|
|
m_impl.reset(new PrimarySurfaceImpl<IDirectDrawSurface>(this));
|
|
m_impl2.reset(new PrimarySurfaceImpl<IDirectDrawSurface2>(this));
|
|
m_impl3.reset(new PrimarySurfaceImpl<IDirectDrawSurface3>(this));
|
|
m_impl4.reset(new PrimarySurfaceImpl<IDirectDrawSurface4>(this));
|
|
m_impl7.reset(new PrimarySurfaceImpl<IDirectDrawSurface7>(this));
|
|
}
|
|
|
|
HRESULT PrimarySurface::flipToGdiSurface()
|
|
{
|
|
CompatPtr<IDirectDrawSurface7> gdiSurface;
|
|
if (!g_primarySurface || !(gdiSurface = getGdiSurface()))
|
|
{
|
|
return DDERR_NOTFOUND;
|
|
}
|
|
return g_primarySurface.get()->lpVtbl->Flip(g_primarySurface, gdiSurface, DDFLIP_WAIT);
|
|
}
|
|
|
|
CompatPtr<IDirectDrawSurface7> PrimarySurface::getGdiSurface()
|
|
{
|
|
if (!g_primarySurface)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
DDSCAPS2 caps = {};
|
|
caps.dwCaps = DDSCAPS_FLIP;
|
|
CompatWeakPtr<IDirectDrawSurface7> surface(g_primarySurface);
|
|
|
|
do
|
|
{
|
|
if (isGdiSurface(surface.get()))
|
|
{
|
|
return CompatPtr<IDirectDrawSurface7>::from(surface.get());
|
|
}
|
|
|
|
if (FAILED(surface->GetAttachedSurface(surface, &caps, &surface.getRef())))
|
|
{
|
|
return nullptr;
|
|
}
|
|
surface->Release(surface);
|
|
} while (surface != g_primarySurface);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
CompatPtr<IDirectDrawSurface7> PrimarySurface::getBackBuffer()
|
|
{
|
|
DDSCAPS2 caps = {};
|
|
caps.dwCaps = DDSCAPS_BACKBUFFER;
|
|
CompatPtr<IDirectDrawSurface7> backBuffer;
|
|
g_primarySurface->GetAttachedSurface(g_primarySurface, &caps, &backBuffer.getRef());
|
|
return backBuffer;
|
|
}
|
|
|
|
CompatPtr<IDirectDrawSurface7> PrimarySurface::getLastSurface()
|
|
{
|
|
DDSCAPS2 caps = {};
|
|
caps.dwCaps = DDSCAPS_FLIP;
|
|
auto surface(CompatPtr<IDirectDrawSurface7>::from(g_primarySurface.get()));
|
|
CompatPtr<IDirectDrawSurface7> nextSurface;
|
|
|
|
while (SUCCEEDED(surface->GetAttachedSurface(surface, &caps, &nextSurface.getRef())) &&
|
|
nextSurface != g_primarySurface)
|
|
{
|
|
surface.swap(nextSurface);
|
|
nextSurface.release();
|
|
}
|
|
|
|
return surface;
|
|
}
|
|
|
|
CompatWeakPtr<IDirectDrawSurface7> PrimarySurface::getGdiPrimary()
|
|
{
|
|
LOG_FUNC("PrimarySurface::getGdiPrimary");
|
|
if (!g_primarySurface || !g_gdiPrimarySurface)
|
|
{
|
|
return LOG_RESULT(nullptr);
|
|
}
|
|
|
|
DDSURFACEDESC2 desc = {};
|
|
desc.dwSize = sizeof(desc);
|
|
g_primarySurface->GetSurfaceDesc(g_primarySurface, &desc);
|
|
|
|
g_monitorRect = Win32::DisplayMode::getMonitorInfo(g_deviceName).rcMonitor;
|
|
g_monitorRect.right = g_monitorRect.left + desc.dwWidth;
|
|
g_monitorRect.bottom = g_monitorRect.top + desc.dwHeight;
|
|
|
|
desc = Gdi::VirtualScreen::getSurfaceDesc(g_monitorRect);
|
|
|
|
DDSURFACEDESC2 prevDesc = {};
|
|
prevDesc.dwSize = sizeof(prevDesc);
|
|
g_gdiPrimarySurface->Lock(g_gdiPrimarySurface, nullptr, &prevDesc, DDLOCK_WAIT, nullptr);
|
|
g_gdiPrimarySurface->Unlock(g_gdiPrimarySurface, nullptr);
|
|
|
|
if (desc.dwWidth != prevDesc.dwWidth ||
|
|
desc.dwHeight != prevDesc.dwHeight ||
|
|
desc.lPitch != prevDesc.lPitch ||
|
|
desc.lpSurface != prevDesc.lpSurface ||
|
|
0 != memcmp(&desc.ddpfPixelFormat, &prevDesc.ddpfPixelFormat, sizeof(desc.ddpfPixelFormat)))
|
|
{
|
|
desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH | DDSD_LPSURFACE | DDSD_PIXELFORMAT;
|
|
if (FAILED(g_gdiPrimarySurface->SetSurfaceDesc(g_gdiPrimarySurface, &desc, 0)))
|
|
{
|
|
return LOG_RESULT(nullptr);
|
|
}
|
|
|
|
if (desc.dwWidth != prevDesc.dwWidth ||
|
|
desc.dwHeight != prevDesc.dwHeight)
|
|
{
|
|
DDraw::RealPrimarySurface::restore();
|
|
}
|
|
}
|
|
|
|
return LOG_RESULT(g_gdiPrimarySurface.get());
|
|
}
|
|
|
|
CompatWeakPtr<IDirectDrawSurface7> PrimarySurface::getPrimary()
|
|
{
|
|
return g_primarySurface;
|
|
}
|
|
|
|
HANDLE PrimarySurface::getFrontResource()
|
|
{
|
|
return g_frontResource;
|
|
}
|
|
|
|
HANDLE PrimarySurface::getGdiResource()
|
|
{
|
|
return g_gdiDriverResource;
|
|
}
|
|
|
|
RECT PrimarySurface::getMonitorRect()
|
|
{
|
|
return g_monitorRect;
|
|
}
|
|
|
|
DWORD PrimarySurface::getOrigCaps()
|
|
{
|
|
return g_origCaps;
|
|
}
|
|
|
|
template <typename TSurface>
|
|
static bool PrimarySurface::isGdiSurface(TSurface* surface)
|
|
{
|
|
return surface && DirectDrawSurface::getRuntimeResourceHandle(*surface) == g_gdiRuntimeResource;
|
|
}
|
|
|
|
template bool PrimarySurface::isGdiSurface(IDirectDrawSurface*);
|
|
template bool PrimarySurface::isGdiSurface(IDirectDrawSurface2*);
|
|
template bool PrimarySurface::isGdiSurface(IDirectDrawSurface3*);
|
|
template bool PrimarySurface::isGdiSurface(IDirectDrawSurface4*);
|
|
template bool PrimarySurface::isGdiSurface(IDirectDrawSurface7*);
|
|
|
|
void PrimarySurface::restore()
|
|
{
|
|
LOG_FUNC("PrimarySurface::restore");
|
|
|
|
Gdi::VirtualScreen::update();
|
|
g_primarySurface = m_surface;
|
|
g_gdiRuntimeResource = DirectDrawSurface::getRuntimeResourceHandle(*g_primarySurface);
|
|
|
|
updateFrontResource();
|
|
g_gdiDriverResource = g_frontResource;
|
|
D3dDdi::Device::setGdiResourceHandle(g_gdiDriverResource);
|
|
|
|
DDSCAPS2 caps = {};
|
|
caps.dwCaps = DDSCAPS_FLIP;
|
|
auto surface(CompatPtr<IDirectDrawSurface7>::from(m_surface.get()));
|
|
HRESULT result = S_OK;
|
|
do
|
|
{
|
|
auto resource = D3dDdi::Device::findResource(DDraw::DirectDrawSurface::getDriverResourceHandle(*surface));
|
|
if (resource)
|
|
{
|
|
resource->setAsPrimary();
|
|
}
|
|
CompatPtr<IDirectDrawSurface7> next;
|
|
result = surface->GetAttachedSurface(surface, &caps, &next.getRef());
|
|
next.swap(surface);
|
|
} while (SUCCEEDED(result) && surface != m_surface);
|
|
|
|
Surface::restore();
|
|
}
|
|
|
|
void PrimarySurface::updateFrontResource()
|
|
{
|
|
g_frontResource = DirectDrawSurface::getDriverResourceHandle(*g_primarySurface);
|
|
}
|
|
|
|
void PrimarySurface::updatePalette()
|
|
{
|
|
if (!s_palette)
|
|
{
|
|
if (DDraw::RealPrimarySurface::isFullscreen())
|
|
{
|
|
Gdi::Palette::setHardwarePalette(Gdi::Palette::getSystemPalette().data());
|
|
}
|
|
return;
|
|
}
|
|
|
|
PALETTEENTRY entries[256] = {};
|
|
PrimarySurface::s_palette->GetEntries(s_palette, 0, 0, 256, entries);
|
|
|
|
if (RealPrimarySurface::isFullscreen())
|
|
{
|
|
Gdi::Palette::setHardwarePalette(entries);
|
|
}
|
|
else
|
|
{
|
|
SetPaletteEntries(g_palette, 0, 256, entries);
|
|
HDC dc = GetDC(g_deviceWindow);
|
|
HPALETTE oldPal = SelectPalette(dc, g_palette, FALSE);
|
|
RealizePalette(dc);
|
|
SelectPalette(dc, oldPal, FALSE);
|
|
ReleaseDC(g_deviceWindow, dc);
|
|
}
|
|
|
|
RealPrimarySurface::scheduleUpdate();
|
|
}
|
|
|
|
void PrimarySurface::waitForIdle()
|
|
{
|
|
if (g_device)
|
|
{
|
|
g_device->waitForIdle();
|
|
}
|
|
}
|
|
|
|
CompatWeakPtr<IDirectDrawPalette> PrimarySurface::s_palette;
|
|
}
|