0

I have a standard win32 window, that I draw on with D2D1. Everything is responsive and runs smoothly. My problem is as follows: After the window is created, it calls WM_PAINT once and then gets "stuck" waiting for any user input (ex. mouse move or click in the window area). As it receives the input, it runs without any further problems. While this doesn't necessarily render the program nonfunctional, it seems highly unprofessional and... ugly. Oh, and what I mean by "stuck" is that the background processes still run without problems, so the loop runs as intended - however, the WM_PAINT isn't called for some reason.

Here is the current code:

header file:

#define PROGRAM_FPS 30
#define GWINDOW_STYLE (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_CLIPCHILDREN | WS_CLIPSIBLINGS)

class Application
{
public:
    Application();
    ~Application();

    // Register the window class and call methods for instantiating drawing resources
    HRESULT Initialize();

    // Process and dispatch messages
    void RunMessageLoop();

    static inline void SafeRelease(IUnknown * _X)
    {
        if(_X != NULL)
            _X->Release();
    };

    HRESULT SetFullscreen(bool);

private:

    // Time (ms) between frames of the application
    // Initiation: m_AppFPS_Div( (DWORD)1000.0 / (DWORD)PROGRAM_FPS )
    const DWORD m_AppFPS_Div;

    // Initialize device-independent resources.
    HRESULT CreateDeviceIndependentResources();

    // Initialize device-dependent resources.
    HRESULT CreateDeviceResources();

    // Release device-dependent resource.
    void DiscardDeviceResources();

    // Draw content.
    HRESULT OnRender();

    HRESULT LoadBitmapFromFile(
        ID2D1RenderTarget*,
        PCWSTR,
        UINT,
        UINT,
        ID2D1Bitmap **
        );

    // The windows procedure.
    static LRESULT CALLBACK WndProc(
        HWND hWnd,
        UINT message,
        WPARAM wParam,
        LPARAM lParam
        );
    HWND m_hwnd;
    ID2D1Factory * m_pDirect2dFactory;
    ID2D1HwndRenderTarget * m_pRenderTarget;
    IWICImagingFactory *m_pIWICFactory;
    ID2D1SolidColorBrush * m_pWhiteBrush;
    ID2D1SolidColorBrush * m_pGrayBrush;
    ID2D1LinearGradientBrush * m_pLinearGradientBrush;
    ID2D1Bitmap *m_LoadingPicture;
};

parts of the .cpp file

void Application::RunMessageLoop()
{
    HACCEL hAccelTable = LoadAccelerators(hInst, MAKEINTRESOURCE(IDC_WIN32APP));
    DWORD screen_last_refresh = 0;
    MSG msg;
    for(bool applicationRunning = true; applicationRunning;)
    {
        while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if(msg.message == WM_QUIT)
            {
                applicationRunning = false;
            }

            if(!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }

        /*** HERE I RUN VARIOUS BACKGROUND PROCESSES ***/

        while((GetTickCount() - screen_last_refresh) < m_AppFPS_Div)
            Sleep(2);
        InvalidateRect(msg.hwnd, NULL, false);
        screen_last_refresh = GetTickCount();
    }
}

LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    LRESULT result = 0;
    if (message == WM_CREATE)
    {
        LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
        Application *pApp = (Application *)pcs->lpCreateParams;

        ::SetWindowLongPtrW(hwnd, GWLP_USERDATA, PtrToUlong(pApp) );
        InvalidateRect(hwnd, NULL, false);
        result = 1;
    }
    else
    {
        Application *pApp = reinterpret_cast<Application *>(static_cast<LONG_PTR>(
            ::GetWindowLongPtrW(
                hwnd,
                GWLP_USERDATA
                )));

        bool wasHandled = false;

        if (pApp)
        {
            switch (message)
            {
            case WM_SIZE:
                {
                    UINT width = LOWORD(lParam);
                    UINT height = HIWORD(lParam);
                    pApp->OnResize(width, height);
                }
                result = 0;
                wasHandled = true;
                break;

            case WM_DISPLAYCHANGE:
                {
                    InvalidateRect(hwnd, NULL, FALSE);
                }
                result = 0;
                wasHandled = true;
                break;

            case WM_PAINT:
                {
                    pApp->OnRender();
                    ValidateRect(hwnd, NULL);
                }
                result = 0;
                wasHandled = true;
                break;

            case WM_DESTROY:
                {
                    PostQuitMessage(0);
                }
                result = 1;
                wasHandled = true;
                break;
            }
        }

        if (!wasHandled)
        {
            result = DefWindowProc(hwnd, message, wParam, lParam);
        }
    }
    return result;
}

HRESULT Application::OnRender()
{
    HRESULT hr = S_OK;
    hr = CreateDeviceResources();
    if (SUCCEEDED(hr))
    {
        m_pRenderTarget->BeginDraw();
        m_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());

        /** HERE I HANDLE THE OPENING ANIMATION **/

        hr = m_pRenderTarget->EndDraw();
    }

    if (hr == D2DERR_RECREATE_TARGET)
    {
        hr = S_OK;
        DiscardDeviceResources();
    }
    return hr;
}

So, I've tested it on 2 computers (both using Win 8.1) with VS 2012 Ultimate and VS 2013 Professional, run debug tests back and forth, stripping certain parts of the program, searched MDSN, Google and StackExchange - to no avail. This freeze also happens when I handle WM_COMMAND of a child window. There was also no such issue when I tried implementing GDI+, but it proved to be highly ineffective, hence the switch to DirectX. Aside from that, the program runs flawlessly. I hope I've provided enough information.

  • Instead of triggering the rendering in your main loop using `InvalidateRect`, why not just call your `OnRender` function directly? – Jonathan Potter Jun 02 '15 at 21:15
  • @JonathanPotter That's what I'm doing as of now actually. The point is, this method works perfectly once I move the mouse or hit a key. I wish to know why that may be the case. – Zero Endorphine Jun 02 '15 at 21:29

1 Answers1

0

You have not been clear about what you are doing to run "various background processes." That is probably where the "stuck" issue comes from. A suggestion is to move that out of the message loop. Instead, call SetTimer during window initialization and then do a bit of your background process work each time the WM_TIMER message comes in.

And, when you want a WM_PAINT to come in call InvalidateRect().

ScottMcP-MVP
  • 10,337
  • 2
  • 15
  • 15
  • By "various background processes" I, of course, meant that they are irrelevant to this issue. And as I've stated, the entire `FOR` loop is functional, so the `InvalidateRect` still gets called 30 times a second without `WM_PAINT` being executed. Step-by-step debug method as well as direct observation reveals just that. The rest of the app acts as if nothing was wrong, and yet refuses to draw. – Zero Endorphine Jun 02 '15 at 20:51
  • 1
    WM_PAINT is a low priority message. It is held until the message queue is empty. If you are continuously processing a stream of other messages WM_PAINT will not come in. – ScottMcP-MVP Jun 03 '15 at 02:36