I'm refactoring my renderer and windowing subsystems to get along nicely with other applications - gracefully handle maximizing, minimizing, restoring, focus changes, etc. Everything seems to be going smoothly according to my tests (still need to check alt+tab), but I do have one problem.
When the app minimizes from windowed mode (it gracefully handles fullscreen to windowed mode transitions,
and it should work just fine on multimon systems, though not multihead; any volunteers to test?), it leaves a region exactly the size of its window that is not properly updated by other apps. Context menus write to it, but that data remains there after the context menu is closed. Other apps can't write to it, so there's a floating rectangle of comparative gibberish - whatever was underneath the window when it was maximized, usually Visual Studio in my case - that remains over all other windows until I terminate my app.
Actually, I cleared all breakpoints to observe behavior just now to ensure that I provide you with accurate data. It doesn't minimize, but it maximizes just fine. Alt+tabbing works fine, and it responds to Close and Restore from the task bar button context menu.
Here's the relevant sources (forgive the formatting; I haven't bothered to set VC++ to use spaces rather than tabs). My message handler (r is a Renderer * and a member of Handler):
struct Handler : MessageHandler
{
// other methods
//
LRESULT operator () (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch(msg)
{
// window state management messages
//
case WM_ACTIVATE:
switch(LOWORD(wparam))
{
case WA_ACTIVE:
case WA_CLICKACTIVE:
active = true;
r->Reset(hwnd);
break;
case WA_INACTIVE:
r->Release();
active = false;
break;
}
return 0;
case WM_MOUSEACTIVATE:
r->SetWindowed(true);
r->Reset(hwnd);
return MA_NOACTIVATE;
case WM_SIZE:
switch(wparam)
{
case SIZE_MAXHIDE:
case SIZE_MAXSHOW:
case SIZE_MINIMIZED:
break;
case SIZE_MAXIMIZED:
r->SetWindowed(false);
case SIZE_RESTORED:
r->Reset(hwnd);
active = true;
break;
}
}
return 0;
case WM_SYSCOMMAND:
switch(wparam)
{
case SC_MAXIMIZE:
r->SetWindowed(false);
r->Reset(hwnd);
active = true;
break;
case SC_RESTORE:
r->SetWindowed(true);
r->Reset(hwnd);
active = true;
break;
case SC_NEXTWINDOW:
case SC_PREVWINDOW:
r->SetWindowed(true);
case SC_MINIMIZE:
active = false;
r->Release();
break;
case SC_MONITORPOWER:
if(2 == lparam)
{
active = false;
r->Release();
}
break;
case SC_CLOSE:
PostMessage(hwnd, WM_CLOSE, 0, 0);
break;
}
return 0;
// other messages
//
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
// other methods
//
};
Relevant methods from my renderer's implementation:
void Renderer::Release()
{
if(indices)
{
indices->Release();
indices = 0;
}
if(vertices)
{
vertices->Release();
vertices = 0;
}
if(device)
{
device->Release();
device = 0;
}
}
void Renderer::Reset(HWND hwnd)
{
Release();
InitDevice(hwnd);
InitBuffers();
}
void Renderer::InitDevice(HWND wnd)
{
int adapter = AdapterFromWindow(wnd);
D3DDISPLAYMODE mode;
GetDisplayMode(adapter, mode);
D3DPRESENT_PARAMETERS presentParams;
ZeroMemory(&presentParams, sizeof(D3DPRESENT_PARAMETERS));
// initialize device. the device may be reinitialized after a context
// change or fullscreen-windowed mode switch or a resolution change from
// the options menu.
//
presentParams.BackBufferCount = 1; // double buffering
presentParams.BackBufferFormat = D3DFMT_A8R8G8B8;
presentParams.hDeviceWindow = wnd;
presentParams.MultiSampleType = D3DMULTISAMPLE_NONE;
presentParams.SwapEffect = D3DSWAPEFFECT_DISCARD;
presentParams.Windowed = windowed;
if(!windowed)
{
// fullscreen-specific initialization.
//
presentParams.BackBufferHeight = mode.Height;
presentParams.BackBufferWidth = mode.Width;
presentParams.FullScreen_RefreshRateInHz = mode.RefreshRate;
}
// TODO: error handling
//
d3d->CreateDevice(adapter, D3DDEVTYPE_HAL, wnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING, &presentParams, &device);
}
void Renderer::InitBuffers()
{
// no buffer initialization as yet
//
}
int Renderer::AdapterFromWindow(HWND wnd)
{
// determine monitor from window. find corresponding adapter.
//
HMONITOR monitor = MonitorFromWindow(wnd, MONITOR_DEFAULTTONEAREST);
for(int adapter = 0; adapter < d3d->GetAdapterCount(); ++adapter)
{
if(d3d->GetAdapterMonitor(adapter) == monitor)
return adapter;
}
// something went wrong, return default.
// TODO: throw exception
//
return D3DADAPTER_DEFAULT;
}
void Renderer::GetDisplayMode(int adapter, D3DDISPLAYMODE & mode)
{
if(windowed)
{
// retrieve current display mode for windowed operation.
//
d3d->GetAdapterDisplayMode(adapter, &mode);
}
else
{
// enumerate available modes for fullscreen. select first valid.
// TODO: robust mode selection
//
D3DFORMAT formats[] = { D3DFMT_A1R5G5B5, D3DFMT_A8R8G8B8,
D3DFMT_R5G6B5, D3DFMT_X1R5G5B5, D3DFMT_X8R8G8B8 };
int formatCount = sizeof(formats);
for(int fmt = 0; fmt < formatCount; ++fmt)
{
int modeCount = d3d->GetAdapterModeCount(adapter, formats[fmt]);
for(int index = 0; index < modeCount; ++index)
{
d3d->EnumAdapterModes(adapter, formats[fmt], index, &mode);
if(SUCCEEDED(d3d->CheckDeviceType(adapter, D3DDEVTYPE_HAL,
mode.Format, D3DFMT_A8R8G8B8, windowed)))
{
// TODO: verify support for desired level of functionality
//
fmt = formatCount;
break;
}
}
}
}
}
void Renderer::SetWindowed(bool state)
{
windowed = state;
}
Sorry about the amount of code. All insights welcome, particularly those that pontificate and ramble about good design and best practices, methodology and so forth. [smile]