News:

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

Main Menu

Win32 My First Window

Started by tda0626, April 24, 2024, 11:05:19 AM

Previous topic - Next topic

Greenhorn

Quote from: tda0626 on May 01, 2024, 10:21:52 AM
Quote from: NoCforMe on May 01, 2024, 09:41:00 AM
Quote from: tda0626 on May 01, 2024, 08:53:56 AMThanks JJ. I feel so stupid  :joking:

You don't need to call GetClientRect(), because guess what?  that PAINTSTRUCT (ps)that you got from BeginPaint() already contains a rectangle which you can then pass to DrawText():
    invoke DrawText, hdc, offset greeting, -1, addr ps.rcPaint, ...


Good to know! Thanks!



Size the window and look what happens.  :toothy:

Add this to solve the issue.

CASE WM_SIZE
     invoke InvalidateRect, hWnd, NULL, TRUE
     xor  eax, eax
     ret
Kole Feut un Nordenwind gift en krusen Büdel un en lütten Pint.

NoCforMe

Yes; to explain what's going on here:
The rectangle passed in the PAINTSTRUCT structure obtained by BeginPaint() contains the area of the window that needs to be painted. This area can be the entire window, or it can be some smaller area.

The way to force a window to repaint itself is a little non-intuitive: you call InvalidateRect() to "invalidate" the contents of the window, which makes the system send the window a WM_PAINT message.

One of the parameters of InvalidateRect() (the 2nd one) is a pointer to a RECT. If you pass NULL here, as Greenhorn showed, then the entire window is repainted (the RECT that gets retrieved by BeginPaint() covers the entire client area of the window).

Since I don't have a working executable of this program, I'm curious: what does happen if you resize the window?
Assembly language programming should be fun. That's why I do it.

jj2007

Quote from: Greenhorn on May 01, 2024, 01:52:04 PMSize the window and look what happens.  :toothy:

Add this to solve the issue.

CASE WM_SIZE
     invoke InvalidateRect, hWnd, NULL, TRUE
     xor  eax, eax
     ret

The program works perfectly and redraws the text in the centre when you size the window. Can you explain which "issue" you want to solve by "processing" WM_SIZE with an InvalidateRect?

Quote from: NoCforMe on May 01, 2024, 02:30:30 PMwhat does happen if you resize the window?

Exactly what is supposed to happen: the window redraws itself.

The only quirk when using ps.rcPaint instead of rect is that when you move the window around, so that part of it goes outside the desktop, then the text gets painted elsewhere, i.e. it won't be centred. This is because WM_PAINT passes a RECT that is smaller than what you get with GetWindowRect.

However, it gets repainted correctly as soon as you size the window: the default WM_SIZE processing takes care of that. No need for a separate handler.

Test yourself:

CASE WM_PAINT
mov hdc, rv(BeginPaint, hWnd, addr ps)
if 1 ; put if 0 to test the second option
invoke GetClientRect, hWnd, addr rect
invoke DrawText, hdc, offset greeting, -1, addr rect, DT_SINGLELINE or DT_CENTER or DT_VCENTER
else
invoke DrawText, hdc, offset greeting, -1, addr ps.rcPaint, DT_SINGLELINE or DT_CENTER or DT_VCENTER
endif
invoke EndPaint, hWnd, addr ps
ret

tda0626

Quote from: Greenhorn on May 01, 2024, 01:52:04 PM
Quote from: tda0626 on May 01, 2024, 10:21:52 AM
Quote from: NoCforMe on May 01, 2024, 09:41:00 AM
Quote from: tda0626 on May 01, 2024, 08:53:56 AMThanks JJ. I feel so stupid  :joking:

You don't need to call GetClientRect(), because guess what?  that PAINTSTRUCT (ps)that you got from BeginPaint() already contains a rectangle which you can then pass to DrawText():
    invoke DrawText, hdc, offset greeting, -1, addr ps.rcPaint, ...


Good to know! Thanks!



Size the window and look what happens.  :toothy:

Add this to solve the issue.

CASE WM_SIZE
     invoke InvalidateRect, hWnd, NULL, TRUE
     xor  eax, eax
     ret


Wouldn't the DefWindowProc take care of those messages and invalidate the window?

jj2007

Quote from: tda0626 on May 01, 2024, 09:10:25 PMWouldn't the DefWindowProc take care of those messages and invalidate the window?

Exactly.

Greenhorn

Quote from: jj2007 on May 01, 2024, 06:23:20 PMThe program works perfectly and redraws the text in the centre when you size the window. Can you explain which "issue" you want to solve by "processing" WM_SIZE with an InvalidateRect?


No, it does not work perfectly. And yes, I've tested it.
The DefWindowProc does not invalidate the whole client area by default, why should it do that?

In case of ".if 1" and sizing the window (smaller/bigger) ...
invoke GetClientRect, hWnd, addr rect
invoke DrawText, hdc, offset greeting, -1, addr rect, DT_SINGLELINE or DT_CENTER or DT_VCENTER



In case of ".if 0" and sizing the window (bigger) ...
invoke DrawText, hdc, offset greeting, -1, addr ps.rcPaint, DT_SINGLELINE or DT_CENTER or DT_VCENTER



The areas within the red rectangles need to be drawn.

NoCforMe pointed to what happens in WM_PAINT. Only the part of the client area that needs to be (re)drawn gets invalidated.
If you size the window smaller, then no repaint is needed and no WM_PAINT is sent to the window.
If you size the window bigger, then ps.rcPaint contains just the parts that needs to be drawn (left or bottom). The rest of the client area remains validated.

That is the reason why you have to invalidate the whole client area when sizing the window in this case.
Kole Feut un Nordenwind gift en krusen Büdel un en lütten Pint.

jj2007

Quote from: Greenhorn on May 02, 2024, 07:31:23 AMThe DefWindowProc does not invalidate the whole client area by default, why should it do that?

I understand your logic, but as a matter of fact, when sizing the window, the text moves to the new centre. At least on my machine (Windows 10) :cool:

The effect of partial invalidation becomes evident only when part of the window is outside the desktop's client area. Then, when sizing, the text moves to the centre of the visible part - as expected.

The easiest solution to get the intended behaviour is indeed GetClientRect.

NoCforMe

Quote from: jj2007 on May 02, 2024, 07:55:48 AMThe easiest solution to get the intended behaviour is indeed GetClientRect.
Yes, agreed. That way you're always repainting the entire client area.

So cancel my suggested "refinement".
Assembly language programming should be fun. That's why I do it.

Greenhorn

Quote from: jj2007 on May 02, 2024, 07:55:48 AMI understand your logic, but as a matter of fact, when sizing the window, the text moves to the new centre. At least on my machine (Windows 10) :cool:

That is because the OP uses CS_HREDRAW or CS_VREDRAW as window class style.
My testing application didn't, so, my fault.  :wink2:

However, the WndProc should be changed.
I'm not familiar with the MASM SDK macros but isn't there something like CASE ELSE ?
The DefWindowProc is called always but shouldn't.
And the code still does not return a proper value after handling a message.

WndProc proc hWnd:HWND, message:UINT, wParam:WPARAM, lParam:LPARAM   
   
    local hdc:HDC
    local ps:PAINTSTRUCT
    local rect:RECT
   
    SWITCH message
           
    CASE WM_PAINT
   
        invoke BeginPaint, hWnd, addr ps
        invoke GetClientRect, hWnd, addr rect
        invoke DrawText, hdc, offset greeting, -1, addr rect, DT_SINGLELINE or DT_CENTER or DT_VCENTER
        invoke EndPaint, hWnd, addr ps
        ret   ; should return zero here
   
    CASE WM_DESTROY
   
        invoke PostQuitMessage, 0
        ret   ; should return zero here
       
    ENDSW
   
        invoke DefWindowProc, hWnd, message, wParam, lParam   ; This is executed always !
       
        ret

WndProc endp
Kole Feut un Nordenwind gift en krusen Büdel un en lütten Pint.

jj2007

Quote from: Greenhorn on May 02, 2024, 09:48:47 AMThe DefWindowProc is called always but shouldn't.

Quote from: jj2007 on April 28, 2024, 06:40:48 PMThere is an old debate, in the asm and C/C++ worlds, whether DefWindowProc should come under the last .else clause (when using chains), or whether it should be placed at the very end. IMHO it should be placed at the end, because there are 3 use cases:

1. You just grab information (size, x pos, ...) from a message; Windows must continue with the default processing.

2. You return a flag to Windows, e.g. zero: xor eax, eax, then ret. No further processing please.

3a. You want to change something, like painting an emoji, but then Windows must continue with the default processing to finalise the task.

3b. Rare case: you want to change something, but first Windows must do its job: call DefWindowProc, then do your emoji painting etc, then decide whether you are done (ret), or whether Windows must continue with the default processing to finalise the task.

All three scenarios work fine with a single invoke DefWindowProc at the very end.

jj2007

Quote from: Greenhorn on May 02, 2024, 09:48:47 AMinvoke EndPaint, hWnd, addr ps
        ret   ; should return zero here

You can return zero, but you can also just continue with DefWindowProc:

invoke GetUpdateRect, hWnd, addr rc, 0
before BeginPaint
rc.left         0
rc.top          0
rc.right        584
rc.bottom       341

invoke GetUpdateRect, hWnd, addr rc, 0
after EndPaint
rc.left         0
rc.top          0
rc.right        0
rc.bottom       0

After EndPaint, there is nothing to update, so DefWindowProc will do nothing. You may save a few cycles by returning, though :cool:

sinsi

Quote from: jj2007 on May 02, 2024, 11:00:35 AMAfter EndPaint, there is nothing to update, so DefWindowProc will do nothing. You may save a few cycles by returning, though
Now, now, jj, you should always obey Microsoft  :eusa_naughty:
Quote from: WM_PAINTAn application returns zero if it processes this message.

NoCforMe

OK, here's how that window-message processing should go. No need for code: use macros, don't use macros, whatever, but make it do this:



Dang it! How do I get images to display larger so you don't have to click on them? I used the "Do not resize my image" option and it still resized it! I know it's possible because I've seen others post larger images.
Assembly language programming should be fun. That's why I do it.

jj2007

Quote from: sinsi on May 02, 2024, 11:21:40 AMNow, now, jj, you should always obey Microsoft  :eusa_naughty:
QuoteAn application returns zero if it processes this message.
Win32 Programmer's Reference, version 50.1.7600.16386:
QuoteAn application should return zero if it processes this message
:smiley:

QuoteThe BeginPaint function automatically sets the clipping region of the device context to exclude any area outside the update region. The update region is set by the InvalidateRect or InvalidateRgn function and by the system after sizing, moving, creating, scrolling, or any other operation that affects the client area. If the update region is marked for erasing, BeginPaint sends a WM_ERASEBKGND message to the window.

An application should not call BeginPaint except in response to a WM_PAINT message. Each call to BeginPaint must have a corresponding call to the EndPaint function.

If the caret is in the area to be painted, BeginPaint automatically hides the caret to prevent it from being erased.

If the window's class has a background brush, BeginPaint uses that brush to erase the background of the update region before returning.

BeginPaint is a very versatile function. What they don't say explicitly is that immediately after the invoke BeginPaint the client area is already validated. So, if instead of returning zero you let DefWindowProc kick in, it will see "nothing to do" and just return.

NoCforMe

But why do that? Sounds like slightly perverse programming to me: just do it like they say to do it and you'll be fine. Why do you want to include DefWindowProc() in that?

Some rules are made to be broken. But then, some rules are better followed ...
Assembly language programming should be fun. That's why I do it.