News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

The mysteries of WM_PAINT, pt. I

Started by NoCforMe, May 13, 2024, 04:49:07 AM

Previous topic - Next topic

NoCforMe

Well, I learned a little something about WM_PAINT just now.
In my li'l VU meter toy demo, I had code like this in my WM_TIMER handler that drove the demo animation:
; Erase previous needle image:
    MOV    NeedleBMPhandle, NULL
    INVOKE InvalidateRect, MeterDispHandle, <addr. of rect.>, FALSE
    .  .  .  .
; Draw next needle image:
    MOV    NeedleBMPhandle, <handle from structure>
    INVOKE InvalidateRect, MeterDispHandle, <addr. of rect.>, FALSE
I threw some logging code into the WM_PAINT handler of the meter-display proc, and was surprised to find that ... my NeedleBMPhandle was never getting set to NULL! (I'm using this value as a flag to indicate that an image needs to be erased rather than drawn.)

I couldn't figure out what was going on here--after all, I was explicitly setting that variable to NULL before the InvalidateRect() call, so why was it never coming up NULL in the WM_PAINT handler?

Then I thought about it a bit more and remembered that WM_PAINT is actually a low-priority message in Windows, and that apparently there was enough delay in handling that paint request (the InvalidateRect()) that by the time it was sent the handle had been reset to non-NULL. And then I ran across this comment in a thread on the topic on Stack Overflow:
QuoteThe 'synthesized from the window state' clause is what makes WM_PAINT and WM_TIMER appear to have a low priority. And why moving the mouse rapidly doesn't flood the message queue with mouse messages. That is however not exclusive, you can for example call UpdateWindow() to force a WM_PAINT message to be sent, making it being dispatched with a 'high priority'.

Well, sure enough, when I changed my code to this:
; Erase previous needle image:
    MOV    NeedleBMPhandle, NULL
    INVOKE InvalidateRect, MeterDispHandle, <addr. of rect.>, FALSE
    INVOKE UpdateWindow, MeterDispHandle
it worked like a charm.

Assembly language programming should be fun. That's why I do it.

kcvinu


Greenhorn

Quote from: NoCforMe on May 13, 2024, 04:49:07 AMWell, sure enough, when I changed my code to this:
; Erase previous needle image:
    MOV    NeedleBMPhandle, NULL
    INVOKE InvalidateRect, MeterDispHandle, <addr. of rect.>, FALSE
    INVOKE UpdateWindow, MeterDispHandle
it worked like a charm.

You can also use RedrawWindow ...
Kole Feut un Nordenwind gift en krusen Büdel un en lütten Pint.

jj2007

#3
I am surprised that you need UpdateWindow after InvalidateRect. Have you tried to set bErase to TRUE?

NoCforMe

I was surprised too. I'll have to go back and check that.
Assembly language programming should be fun. That's why I do it.

Greenhorn

If you want the client area to be redrawn immediately after a call of InvalidateRect you need to call UpdateWindow. Or you take both calls in one step with RedrawWindow ...

; Erase previous needle image:
    MOV    NeedleBMPhandle, NULL
    INVOKE RedrawWindow, MeterDispHandle, <addr. of rect.>, RDW_ERASE or RDW_INVALIDATE or RDW_UPDATENOW
Kole Feut un Nordenwind gift en krusen Büdel un en lütten Pint.