0

I have a window created with the WS_EX_LAYERED window style. I am currently drawing onto a memory bitmap using GDI+, and using UpdateLayeredWindow to update the graphical content of my layered window. I intend to use this window as the main window of my application, which would require it to redraw frequently.

Seeing as layered windows do not receive the WM_PAINT windows message[?], I need to come up with an appropriate method for re-drawing the window. Optimisation is not essential, but it's always nice to have your cake and eat it too. Therefore, I am in search of the "correct" method to use.

Here are my thoughts so far:

  • I would guess that it's a good idea to render onto an off-screen bitmap before BitBlting or similar.

  • 60 frames rendered per second should be (more than?) enough (but how does this compare to other applications' frame rates?).

Possible solutions:

  • Use SetTimer to send the WM_TIMER message on a regular basis.

    • Useful because through specifying the time-out value, I can achieve my desired frames per second, without the requirement to measure the duration a "frame" takes to be rendered.

    • Would likely cause input or other lags due to the frequency and speed of the messages.

  • Render frames only when particular events occur, such as a window resize.

    • Would require me to figure out all events that would require a redraw.

    • Would greatly reduce the amount of unnecessary frames being rendered.

  • Render frames when there are no messages in the message queue, by checking PeekMessage.

    • This might slow down the processing of window messages.

    • This will cause a high CPU usage because more frames than necessary are being processed.

  • Create a new thread to perform the render loop.

    • Timing calculations will have to be performed in order to maintain a steady frame-rate.
Spooky
  • 2,966
  • 8
  • 27
  • 41
  • Are stuck to UpdateLayeredWindow? It is possible to create a layered Window with WM_PAINT with SetLayeredWindowAttributes. If osible using this you get rid of the question of to prepare an update mechanism. – xMRi Oct 07 '13 at 10:12
  • 1
    "how does this compare to other applications' frame rates?" Unless an application is animating something or displaying a video its frame rate will typically be zero. To repeatedly repaint the screen for no reason at all is to steal CPU time from more productive uses, to flatten laptop batteries, to wear out fans and to make yourself a target for uninstallation. Seriously, if your app isn't doing anything, don't do anything. – arx Oct 07 '13 at 12:26

2 Answers2

1

Layered windows don't receive WM_PAINT messages that would otherwise be generated after window visibility changed, but it won't prevent them to receive this message at all.

You can continue to use InvalidateRect to change window update region, wait for WM_PAINT in you window procedure, draw contents in bitmap and call UpdateLayeredWindow to change window contents. You can use this method to request redraw when content of window changes, for example, when button was pressed, or window has been resized (or activated/deactivated).

weaknespase
  • 1,014
  • 8
  • 15
0

It shouldn't be so complicated, this is the pseudo code for you message loop:

while (true) 
{
    // GetMessages
    while (PeekMessage(&msg, hWnd, 0, 0, PM_NOREMOVE))
    {
        if (!GetMessage(&msg, hWnd, 0, 0 ))
        {
            // Need to handle WM_QUIT
            ...
            break;
        }
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }

    // Check if we need to draw
    if (TimeForANewFrameHasCome() ||
        IfWeNeedToDrawAfterInputOrInvalidate() ||
        AnyOtherCaseThatCausesAnUpdate())
    {
         // Render
         UpdateMemoryDCOrBitmap(...);

         // Display it
         UpdateLayeredWindow(...);
    }

    // May sleep a while
    // Either Sleep(20); or better MsgWaitForMultipleObjects, that makes it possible
    // to wake up upon an Event too... 
    MsgWaitForMultipleObjects(...);
}
xMRi
  • 14,982
  • 3
  • 26
  • 59
  • Why are you using `PeekMessage(PM_NOREMOVE)` and `GetMessage()` together? If you use `PeekMessage(PM_REMOVE)` instead, you don't need `GetMessage()` at all. If there is nothing to peek, `PeeMessage()` returns FALSE, so the `while` loop still works. – Remy Lebeau Oct 07 '13 at 20:12
  • I agree. But with this way I don't have to use an extra handling for WM_QUIT. Yes you can just use PM_REMOVE and check for WM_QUIT after the peek. It is jus a matter of style. – xMRi Oct 08 '13 at 06:06
  • Hmmm, let's see... peek+remove a message and look at its ID directly - vs - peek a message but leave it in the queue, then go back to the queue and peek+remove the same message, and have the API look at the ID for you and return a BOOL that you have to check. Yeah, same effect ... but more code and more work for the API. But like you said, it is "just a matter of style". Personally, I don't think the API should do more work than it has to, but that is just my style :) – Remy Lebeau Oct 08 '13 at 07:50
  • Agree... now if I look at the code I would change it to the way you recommended. – xMRi Oct 08 '13 at 08:19