Window focus issue - winapi

I am currently programming a graphics application with OpenGL and the Windows API in C++. Unfortunately the image freezes under certain conditions, such as when I'm resizing the window, and/or when my mouse isn't moving. Is there some sort of mechanism I can use in Win32 to ensure that the frames are constantly being processed?
Here's some pseudocode describing the basic flow of my program
Main Loop
while(running)
{
if (PeekMessage(&Msg,NULL,0,0,PM_REMOVE))
{
if (Msg.message==WM_QUIT)
{
SetRunning(false);
}
else
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
}
else
{
SwapBuffers(deviceContext);
}
}
WndProc
switch(msg)
{
case WM_CLOSE:
{
PostQuitMessage(0);
break;
}
case WM_SIZE:
{
ResizeScreen(LOWORD(lParam),HIWORD(lParam));
break;
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
EDIT: I read the tutorial Kol linked to and made some edits, and now the frame rate is consistent even when the mouse is not moving. However the image still freezes when I'm moving or resizing the window, so I'd appreciate help on that.

Read the NeHe site to learn the basics of OpenGL with Win32. There are detailed explanations about how the message loop should look like, what the WM_SIZE handler should do etc.
EDIT
The code which draws the scene and the buffer swapping should be put into the message loop, in an else branch after the if (PeekMessage(...)) branch. See where the DrawGLScene() call is in the above mentioned NeHe example.
EDIT2
The problems were the followings:
The scene renderer function was not called in the WM_SIZE and WM_MOVE handlers.
The scene was drawn only once a second.

Related

How should I handle the update rect/region/area in a Direct2D application?

In a traditional Windows program that uses GDI for graphics, you would have to worry about only drawing the area of the window that needs to be redrawn; this is the "update rect" and is accessed either by PAINTSTRUCT.rcPaint or by a call to GetUpdateRect(). (This is also available as an HRGN through other means.)
Do I need to do the same thing with Direct2D? All the examples on MSDN just draw the entire client area indiscriminately and searching online hasn't turned up anything else.
Or in other words, would anything bad happen to parts outside the update rect if I only draw within the update rect, for instance either manually or with PushAxisAlignedClip() or PushLayer()?
Furthermore, the documentation for ID2D1HwndRenderTarget::Resize() says
After this method is called, the contents of the render target's back-buffer are not defined, even if the D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS option was specified when the render target was created.
Does this mean that whatever update region would be caused by resizing (such as shown by this picture from this page) is invalid and I should redraw the whole window (for instance, by calling InvalidateRect(NULL)) on a resize?
Thanks.
Yes. Use PushAxisAlignedClip with D2D1_ANTIALIAS_MODE_ALIASED.
Call ID2D1HwndRenderTarget::Resize when the window is resized. Do pay attention to the HRESULT it returns. It can return D2DERR_RECREATE_TARGET, but you may not know that it can also return D2DERR_DISPLAY_STATE_INVALID (which can also be returned by EndDraw, btw). And yes, call InvalidateRect(NULL) after that.
I also recommend using D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS because otherwise you'll run into nasty bugs on some stupid driver/hardware configurations and in other events. Don't ask me why -- it just doesn't behave well without this. You'll get bug reports that your whole rendering area just becomes filled with black. It took me months to figure out that all I had to do was to use that flag. I was never able to repro the problem locally.
No. You have to do near the same thing as normal with gdi. Instead of using a HBITMAP backbuffer, you have to use d2d bitmap. In wm_size, you resize and redraw your d2d bitmap, and in wm_paint instead of bitblt a hbitmap you have to use the render drawbitmap method. and render just the part from the paintstruct rect member (hope this help you) :
Global or class members :
ID2D1Factory* g_pD2DFactory = NULL;
ID2D1HwndRenderTarget* g_pRenderTarget = NULL;
ID2D1SolidColorBrush* g_pBlackBrush = NULL;
ID2D1SolidColorBrush* g_pWhiteBrush = NULL;
ID2D1BitmapRenderTarget* g_bitmapRenderTarget = NULL; //for d2d bitmap
The global or class static wndproc:
case WM_CREATE: {
if (FAILED(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &g_pD2DFactory))) {
throw;
}
LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
HRESULT hr = g_pD2DFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(hWnd, D2D1::SizeU(lpcs->cx, lpcs->cy)),
&g_pRenderTarget
);
if (FAILED(hr)) {
throw;
}
if (FAILED(g_pRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &g_pBlackBrush))) {
throw;
}
if (FAILED(g_pRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), &g_pWhiteBrush))) {
throw;
}
break;
}
case WM_SIZE: {
if(FAILED(g_pRenderTarget->Resize(D2D1::SizeU(LOWORD(lParam), HIWORD(lParam))))) {
throw;
}
D2D_SAFE_RELEASE(g_bitmapRenderTarget)
g_pRenderTarget->CreateCompatibleRenderTarget(&g_bitmapRenderTarget);
g_bitmapRenderTarget->BeginDraw();
g_bitmapRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::AliceBlue));
g_bitmapRenderTarget->DrawEllipse(D2D1::Ellipse(D2D1::Point2F(100,100), 50,50), g_pBlackBrush);
g_bitmapRenderTarget->EndDraw();
break;
}
case WM_PAINT: {
HDC hDc;
PAINTSTRUCT ps;
LPCRECT lpRect;
ID2D1Bitmap* bitmap;
D2D1_RECT_F d2d1Rect;
hDc = BeginPaint(hWnd, &ps);
lpRect = &ps.rcPaint;
d2d1Rect = D2D1::RectF(lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
g_bitmapRenderTarget->GetBitmap(&bitmap);
g_pRenderTarget->BeginDraw();
g_pRenderTarget->DrawBitmap(
bitmap, d2d1Rect, 1.0f, D2D1_BITMAP_INTERPOLATION_MODE::D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,
d2d1Rect
);
g_pRenderTarget->EndDraw();
EndPaint(hWnd, &ps);
return 0;
}

CListCtrl mouse events not working

I have a CListCtrl in a CDialog. And most of the events are not getting called for CListCtrl.
For example OnMouseMove is no getting called when my mouse pointer is on CListCtrl but works if mouse pointer is on window or editcontrol etc.
Note: my CListCtrl is set Report view.
Can anyone explain this behavior?
I have just suffered similar symptoms, (reported in question "MFC CListCtrl does not appear after minimise-restore" under my name). I found exactly as you did, that many messages do not appear where you think they should, some not at all. And others have found the same thing. I solved that by creating my own class inheriting from CListCtrl and just overriding OnNotify(...). I then found I received the messages, trapped only the ones I wanted and revised the behaviour to suit in my own class. (I was simply preventing resizing of column widths.) No other code was needed in my case.
BOOL CCompilationListCtrl::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
HD_NOTIFY *pHDN = (HD_NOTIFY*)lParam;
{
if(pHDN->hdr.code == HDN_BEGINTRACKW || pHDN->hdr.code == HDN_BEGINTRACKA)
{
*pResult = TRUE;
return TRUE;
}
if(pHDN->hdr.code == HDN_ENDTRACKW || pHDN->hdr.code == HDN_ENDTRACKA)
{
*pResult = TRUE;
return TRUE;
}
if(pHDN->hdr.code == HDN_DIVIDERDBLCLICKW || pHDN->hdr.code == HDN_DIVIDERDBLCLICKA)
{
*pResult = TRUE;
return TRUE;
}
}
return CListCtrl::OnNotify(wParam, lParam, pResult);
}

MFC application gui hangs when some data processing

I have application with GUI but it hangs, when i'am clicking the button that actually does all work. What to do to make it run normally and displaying gui changes "online"?
If you don't want to work with background threads and it's a loop of some kind taking so long, you could add this member function to your UI code and call it in the loop:
void CMyDlg::PumpMessages()
{
// Must call Create() before using the dialog
ASSERT(m_hWnd!=NULL);
MSG msg;
// Handle dialog messages
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if(!IsDialogMessage(&msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}

Two dialogs in one MFC application

I am trying to make a dialog based MFC application , where two dialogs needs to be shown sequentially.
What that means is , once the first dialog(modal) is shown and dismissed (by pressing OK), the second dialog needs to be brought up.My requirement is the second dialog should be modeless.
But what I observe is the second dialog is shown but none of the message handling function are being called in response of user messages.I think the message map itself is not working, while the overridden functions(like OnInitdialog) are being called. I tried replacing this modeless dialog with a modal one , and alas, the doModal() itself fails.
Here is the little code:
CFirstDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
// dismissed with OK
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
CSecondDlg *dlgModeLess = new CSecondDlg();
dlgModeLess->Create(CSecondDlg::IDD,NULL);
m_pMainWnd = dlgModeLess;
dlgModeLess->ShowWindow(SW_SHOW);
dlgModeLess->UpdateWindow();
}
Here is the message map of the second dialog:
BEGIN_MESSAGE_MAP(CSecondDlg, CDialog)
ON_MESSAGE(TRAY_MESSAGE,OnTrayMessage)
ON_BN_CLICKED(IDOK, &CSecongDlg::OnBnClickedOk)
ON_BN_CLICKED(IDC_RADIO1, &CSecondDlg::OnBnClickedRadio1)
END_MESSAGE_MAP()
I think I am doing something conceptually wrong. Kindly share your thoughts on what need to be done to tackle such a scenario.
As I mentioned in a previous post, it is not necessary that the second dialog be non modal.
Just do something like this:
BOOL CMyTestApp::InitInstance()
{
CMyTestDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
COtherDlg Dlg ;
m_pMainWnd = &dlg;
if (Dlg.DoModal() == IDCANCEL)
{
...
}
}
else if (nResponse == IDCANCEL)
{
...
}
return FALSE;
}
When you create a modeless dialog box, control will return to your calling function right away, so you need to declare variable dlgModeLess at a global scope and make sure your program/scope is still active until the dialog box finishes
I have solved this problem and this turned out to be interesting.
It seems that Cdialog::Create() itself is not sufficient for creating a fully operative modeless dialog box. We have to supply a win32 style message loop to it.
So this effectively makes two message loops in the program, one provide my MFC framework and the one that I wrote after returning from IDOK. Here is the modified code.
CSecondDlg *dlgModeLess = new CSecondDlg();
dlgModeLess->Create(CSecondDlg::IDD,NULL);
CTrayIconDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
// dismissed with OK
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
MSG leftmsg;
PeekMessage(&leftmsg,m_pMainWnd->m_hWnd,0,0,PM_REMOVE);
MSG msg;
BOOL bRet;
while ((bRet = GetMessage(&msg, dlgModeLess->m_hWnd, 0, 0)) != 0)
{
if (bRet == -1)
{
// Handle the error and possibly exit
}
else if (!IsWindow(hWnd) || !IsDialogMessage(hWnd, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
This code works as expected, The interesting thing to note here is the PeekMessage function which removes a WM_QUIT message that is inserted in the thread's message queue once the first dialog is dismissed as we do not want to quite at that point of time. This i believe is done by the MFC framework.

what the pattern here in the event loop?

In the following code
Draw();
while (WM_QUIT != msg.message)
{
msg = PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE);
if (msg)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Draw();
}
why draw was called inside and outside the event loop?
That's a typical game loop. It constantly draw frames, the PeekMessage() call ensures Windows messages are dispatched as normal, typically mouse and keyboard input to make the game interactive.
It looks like it's called to do an initial draw before it starts the message pump. If there are no messages, it will have drawn something.

Resources