Questions about WM_PAINT
hey I just started learning win API via an online tutorial. Just so I actually learned what I was reading I figured I would play with the code and try to make a snake game adding different features as I went through each lesson. The tutorial says to put the WM_PAINT definition in the WindowProcedure function as a case. I did this and it draws the picture fine. I noticed the bitblt() function had the x and y parameterss of where the item is drawn so I replaced them with code from my class.
Then I added cases in the WM_KEYDOWN section such as MySnake.SnakeMoveUp() when up is pressed. I did not use any velocities yet as I was just testing moving the image. So if you key up then the snake's Y int is reduced to give a smaller coordinate next time the MySnake.GetSnakeY() function is called. Well the picture never moved.
BitBlt(hdc, MySnake.GetSnakeX(), MySnake.GetSnakeY(), bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
I used message boxes inside my code to make sure the proper keys were working such as "you pressed up!" When the key is pressed. I tried putting a post message function thinking maybe WM_PAINT did not update unless told. That did not work so I googled WM_PAINT and read that it was called anytime you update the window. So I added UpdateWindow(hwnd). That did not work. I think I am missing something conceptually about the paint feature. Any ideas?
Ps sorry I did not put much code in but I don't have internet where I am coding so I am using a phone to post this.
UpdateWindow will only cause a WM_PAINT if Windows thinks the window needs to be drawn. If you've just had a WM_PAINT, chances are it doesn't so UpdateWindow won't do anything. You can tell Windows to send one anyway by using a different function, called RedrawWindow. Use it like RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW);
That causes the whole of your window to be redrawn. There are more efficient ways of drawing such as only invalidating the rectangle that's changed, and using permanent double buffering (doing all your updating to a CompatibleDC and using WM_PAINT to BitBlt only the update rectangle to the BeginPaint hdc)
Thanks. Apparently that was the issue. The tutorial i was using was very short and basic and did not really explain many of the functions I was using at all. I am going to have to get more details on the DC and how to draw/ clear it and the double buffering you were talking about to keep from having screen flicker issues. I probably need to add a timer function as well to control the speed of the snake moving across the screen.
WM_PAINT msgs are the lowest priority message in the OS msg queue, and are the only msg that can be pre-processed (all WM_PAINT msgs are examined, the area concatenated and returned to the OS queue).
Also note that WM_PAINT msgs can be generated by events outside your app, like another window crossing your window.
For these reasons you do not want your WM_PAINT handler to contain anything but very simple drawing (none of mine ever contain anything but a single BitBlit()).
To get a speedy draw you need to;
1. InvalidateRect() the smallest area you can. If you not set an area / rect as 'invalid' then no paint will happen.
2. Call UpdateWindow() immediately after the InvalidateRect(). This sends the WM_PAINT directly to the window's callback, bypassing OS and app msg queue.
3. Ensure your paint uses the RECT param from the paint struct filled in by BeginPaint(), so it only redraws the minimum area, not entire client (ie if another window crosses a corner of your window, you only redraw the corner, not the entire client).
To reduce flicker, handle WM_ERASEBKGND and return just TRUE.
Not sure what you mean by handle WM_ERASEBKGND. Add this to the window proc function I am guessing? then are you supposed to make the call to it as well? (PostMessage() maybe?). I ended up just making a MySnake.DrawSnake(HWND h) function to handle all the drawing and I make a call to it whenever the snake changes coordinates. Getting a class body vector to follow and pass direction commamds to the next segment in line has become a little more difficult than I expected. Thanks for the info!
In your callback you create message 'handlers', bits of code to process messages (like your WM_PAINT code).
If you do not have a handler then the code drops through the callback and the OS does default/standard/normal processing.
Each message has a return value which tells the OS that your code has processed the message (or not).
Generally this return is TRUE = I have processed this msg, don't do anything OS (or FALSE = process this OS, because my app has not). You have to check MSDN for exceptions to this general rule however.
So by 'handle WM_ERASEBKGND' I mean create some code that does nothing but tell the OS not to process the message.
return true;//check this in MSDN as I have not coded much WIN32 in recent years