Я работаю над приложением 3D-редактора, используя Direct3D и WinAPI. Я создаю главное окно, которое имеет дочернее окно, которое занимает часть клиентской области главного окна. Это используется D3D в качестве цели рендеринга. Затем я также создаю отдельное окно с CreateWindow
, которое строится так же, как и основное окно (т. Е. «Главное окно» и внутренний дочерний элемент, используемый в качестве цели рендеринга), и я делаю это окно дочерним по отношению к основному. окно приложения (чтобы минимизировать / восстановить / закрыть их вместе).
Рендеринг D3D выполняется дочерними окнами рендеринга, обрабатывающими их сообщения WM_PAINT
. Чтобы уменьшить ненужные накладные расходы, я настроил оконные процедуры так, чтобы они отображались только на WM_PAINT
, если GetForegroundWindow
и GetFocus
совпадают с соответствующими дескрипторами окна. Другими словами, я хочу, чтобы рендеринг окна обновлялся только в том случае, если он находится сверху и сфокусирован.
Это мой основной цикл сообщений:
HWND mainWnd;
HWND mainRenderWnd;
HWND childWnd;
HWND childRenderWnd;
// ...
MSG msg = {};
while (WM_QUIT != msg.message)
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
if (!TranslateAccelerator(mainWnd, accel, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
// Run non-UI code...
}
}
Когда основное окно получает WM_SETFOCUS
, Я установил фокус на его дочернее окно цели рендеринга, так как я хочу обрабатывать входные данные там (например, элементы управления камерой):
// ...
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
//...
case WM_SETFOCUS:
{
SetFocus(mainRenderWnd);
return 0;
}
//...
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
Я делаю то же самое в процедуре окна для childWnd
, установив фокус на childRenderWnd
. Это окно открывается и закрывается пользователем, то есть в любое время оно может существовать или не существовать, но когда оно существует и не свернуто, оно должно быть окном переднего плана с фокусом. Кроме того, чтобы контролировать частоту кадров для дочернего окна, я использую таймер для его обновления:
static constexpr UINT_PTR RENDER_TIMER_ID = (UINT_PTR)0x200;
void TimerCallback(HWND Arg1, UINT Arg2, UINT_PTR Arg3, DWORD Arg4)
{
if (IsIconic(childWnd) || !(GetFocus() == childRenderWnd))
return;
// Invalidate the render area to make sure it gets redrawn
InvalidateRect(childRenderWnd, nullptr, false);
}
// ...
SetTimer(childWnd, RENDER_TIMER_ID, 16, (TIMERPROC)TimerCallback);
Со всей этой настройкой mainWnd
и mainRenderWnd
, кажется, работают просто отлично. Однако childRenderWnd
отказывается отображать что-либо, когда оно находится на переднем плане и в фокусе. Во время отладки я обнаружил, что, хотя это так, обратный вызов таймера никогда не выполняется, и сообщение WM_TIMER
не отправляется в дочернее окно.
С другой стороны, в тот момент, когда я намеренно перемещаю фокус из дочернего окна в главное окно (при сохранении обоих открытых), отправляется сообщение таймера и выполняется обратный вызов. Другая проблема заключается в том, что когда я минимизирую приложение, когда оба окна открыты, а затем восстанавливаю их оба, цель рендеринга ни одного из окон не обновляется. Вместо этого кажется, что фокус «перевернулся», так как я должен сначала щелкнуть по моему дочернему окну, а затем по главному окну, и это заставляет его обновиться должным образом (в то время как ребенок все еще отказывается что-либо визуализировать).
Чего мне не хватает? Я искал других, у которых возникла проблема, например, неправильная блокировка установки сообщения WM_TIMER
, но, кажется, ничто не объясняет, что здесь происходит.
Заранее спасибо за помощь!