I'm trying to resize a graphics device buffer when the window is resized, but I have no luck in detecting the event.
This is C++ Windows programming. I tried:
while(WM_QUIT != msg.message){
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){
switch(msg.message){
case WM_SIZE:
return; //<-- If the program closes because of this return, then I know I found the right statements.
}
//TranslateMessage(&msg);
//DispatchMessage(&msg);
}else{
poly.setConstantBuffer(space.getCamera());
poly.draw(iSize);
graphics.render();
}
}
It is not returning, so that means that is not correct. What is the right way to catch the resize event?
You should be handling messages in your window procedure, not in the message loop. PeekMessage does not return sent messages, and WM_SIZE is a sent message.
WM_SIZE is not the only message sent to a window during a resize. You need to uncoment the calls to TranslateMessage() and DispatchMessage() so those other messages get processed. And your window's mssage dispatch procedure needs to pass unhandled messages to DefWindowProc() for default processing. Are you doing all of that?
Related
I wanted to read out messages in my message loop right before I dispatch them to my window procedure. Most messages I tried reading like this were read correctly, but when I close the window, a WM_CLOSE or WM_DESTROY message could not be read as it seems as if they were never received. Here's what i do:
void Framework::Run(){
while(running){
MSG msg;
while(PeakMessage(&msg, NULL, 0, 0)){
TranslateMessage(&msg);
switch(msg.message){
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_QUIT:
running = false;
break;
//...other cases...
}
DispatchMessage(&msg);
}
//...
}
}
I put a breakpoint at the first case, but even when I close the window (by clicking the 'X') the breakpoint is never hit. Though, when I check for WM_DESTROY in the window procedure, it gets read and every thing goes fine. Why not outside it?
Are such messages sent directly to the window proc? How?
A message loop only sees messages that are posted to the calling thread's message queue. Not all messages go through the message queue. WM_DESTROY is one such message. What you should be doing instead is handling the messages in the window procedure so you see every message that the window receives, whether the message went through the message queue or not.
If you need to look at messages for a window you are not creating yourself, or for a standard window that has a system-provided window procedure, you can subclass the window using SetWindowLongPtr(GWLP_WNDPROC) or SetWindowSubclass(). See Subclassing Controls for more details.
I make small game and have some problem with low priority window messages, that incessantly sending from system and block running game logic's code.
I create my message loop something like this:
bool Window::SystemRoutineAndCheckQuit() {
::MSG msg;
while( ::PeekMessage( &msg, nullptr, 0, 0, PM_REMOVE ) ) {
if( msg.message == WM_QUIT ) {
::UnregisterClass( registeredClassName, ::GetModuleHandle( nullptr ) );
DLOG( lg ) << "Exit from system loop" << DLOG_END;
return false;
}
::TranslateMessage( &msg );
::DispatchMessage( &msg );
}
return true;
}
//....
while( window.SystemRoutineAndCheckQuit() ) {
// do all render and logic
}
I.e. before every frame I wanna process all messages from windows and then, when queue will empty do all logic. I notice when window resizing I get same message WM_SIZING again and again and queue never will empty when mouse button still press (even when size of window don't change from previos call I receive message with same window coordinates). So it block execute my code.
Is there any other messages, that keep windows message queue don't empty and what is the best way to process all messages without some low priority, like WM_SIZING?
I test it on Windows 8.
PS: I need resize window, so I don't wanna disallow it by change style.
EDIT: sorry for incorrect description problem, but found what real happened (and why my previously attempt to fix it by limit number of processed messages, or by break message process when get same message two times in sequence will not success). From PeekMessage I get message WM_NCLBUTTONDOWN and after this message program don't return from ::DispatchMessage and block continue execution of thread until mouse will be released. And when program in DispatchMessage, my handler get repeatly WM_SIZING or WM_MOVING (independently what return from message handler function (result of DefWindowProc, TRUE (message was processed) or 0).
You could just process N messages before each frame.. where N is a limit you set between 1-10. That way you can process a reasonable number of events, then get onto your actual game logic.
It's conceivable that Windows may just generate the 'window sizing' message continually when the queue is empty, to make sure applications are aware the window is being resized.
(Maybe MS think applications might not know, maybe there's nothing else the user can do.. so MS want to hammer that single message.)
When DefWindowProc handles WM_SYSCOMMAND with either SC_MOVE or SC_SIZE in the wParam, it enters a loop until the user stops it by releasing the mouse button, or pressing either enter or escape. It does this because it allows the program to render both the client area (where your widgets or game or whatever is drawn) and the borders and caption area by handling WM_PAINT and WM_NCPAINT messages (you should still receive these events in your Window Procedure).
It works fine for normal Windows apps, which do most of their processing inside of their Window Procedure as a result of receiving messages. It only effects programs which do processing outside of the Window Procedure, such as games (which are usually fullscreen and not affected anyway).
However, there is a way around it: handle WM_SYSCOMMAND yourself, resize or move yourself. This requires a good deal of effort, but may prove to be worth it. Alternatively, you could use setjmp/longjmp to escape from the Window Procedure when WM_SIZING is sent, or Windows Fibers along the same lines; these are hackish solutions though.
I solved it (using the first method) this past weekend, if you're interested I have released the code to the public domain on sourceforge. Just make sure to read the README, especially the caveat section. Here it is: https://sourceforge.net/projects/win32loopl/
Probably just a gross oversight of some sort, but I'm not receiving any WM_SIZE messages in the message loop. However, I do receive them in the WndProc. I thought the windows loop gave messages out to WndProc?
LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
switch(message)
{
// this message is read when the window is closed
case WM_DESTROY:
{
// close the application entirely
PostQuitMessage(0);
return 0;
} break;
case WM_SIZE:
return 0;
break;
}
printf("wndproc - %i\n", message);
// Handle any messages the switch statement didn't
return DefWindowProc (hWnd, message, wParam, lParam);
}
... and now the message loop...
while(TRUE)
{
// Check to see if any messages are waiting in the queue
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// translate keystroke messages into the right format
TranslateMessage(&msg);
// send the message to the WindowProc function
DispatchMessage(&msg);
// check to see if it's time to quit
if(msg.message == WM_QUIT)
{
break;
}
if(msg.message == WM_SIZING)
{
printf("loop - resizing...\n");
}
}
else
{
//do other stuff
}
}
If a message is sent by the system to your window while it's in DefWindowProc or elsewhere in that netherworld that's the Windows message queue, then that message is not going to be seen by your message loop at all.
Note that this is only true for sent messages. Posted messages will show up in the message loop.
If you want to filter all messages, use SetWindowsHookEx with your thread ID, and the appropriate hook type. Or better yet, process them properly in your WndProc.
While you've got hold of the sizing gripper, I believe that Windows is running its own message loop. That will dispatch to your message queue, but your loop is out of the picture while the sizing is ongoing.
The frame window will call SetCapture to capture all subsequent mouse messages. Then it'll resize your window as the mouse moves. It'll also pump the message loop; you can see some similar code here: ftp://ftp.ringdale.com/support/Nlynx/Tech%20Support%20Docs/Midrange/EmeraldSeries/ADK/DDE/C/APITERM/TRACK.C. Note the message loop in the middle of that function.
It pumps the queue itself so that the sizing code doesn't have to return until after the resize tracking is complete.
Edit: I bring up the tracking rectangle code since that's how window resizing used to work, showing just a thin rectangular outline of the window, until we got dynamic window resizing where the entire window updates on the fly while you resize. The behavior internally is likely similar.
Edit 2: Still, credit to the guys who mentioned posted vs sent messages... sent messages won't ever go through the message pump. Sent messages quickly boil down to a function call of your wnd proc. Unless they're sent to windows owned by a different thread, which becomes a lot more complex; they get added to an internal queue belonging to the destination thread's message queue, and are processed internally - before posted messages are returned -inside GetMessage. Getting the sent message's return value back to the source thread involves more gyrations :)
WM_SIZING and WM_SIZE are not the same message. I think ordinary mouse operations to resize a window send WM_SIZING first, but if some program sends a WM_SIZE message then you're only going to get WM_SIZE without WM_SIZING.
I was wondering what the difference between the WM_QUIT, WM_CLOSE, and WM_DESTROY messages in a windows program, essentially: when are they sent, and do they have any automatic effects besides what's defined by the program?
They are totally different.
WM_CLOSE is sent to the window when it is being closed - when its "X" button is clicked, or "Close" is chosen from the window's menu, or Alt-F4 is pressed while the window has focus, etc. If you catch this message, this is your decision how to treat it - ignore it, or really close the window. By default, WM_CLOSE passed to DefWindowProc() causes the window to be destroyed.
WM_DESTROY is sent to the window when it starts to be destroyed. In this stage, in opposition to WM_CLOSE, you cannot stop the process, you can only make any necessary cleanup. When you catch WM_DESTROY, none of its child windows have been destroyed yet.
WM_NCDESTROY is sent to the window when it is finishing being destroyed. All of its child windows have been destroyed by this time.
WM_QUIT is not related to any window (the hwnd got from GetMessage() is NULL, and no window procedure is called). This message indicates that the message loop should be stopped and the application should exit. When GetMessage() reads WM_QUIT, it returns 0 to indicate that. Take a look at a typical message loop snippet - the loop is continued while GetMessage() returns non-zero.
WM_QUIT can be sent by the PostQuitMessage() function. This function is usually called when the main window receives WM_DESTROY (see a typical window procedure snippet).
First of all, the WM_CLOSE and WM_DESTROY messages are associated with particular windows whereas the WM_QUIT message is applicable to the whole application (well thread) and the message is never received through a window procedure (WndProc routine), but only through the GetMessage or PeekMessage functions.
In your WndProc routine the DefWindowProc function takes care of the default behavoir of these messages. The WM_CLOSE messages requests that the application should close and the default behavoir for this is to call the DestroyWindow function. Its when this DestroyWindow function is called that the WM_DESTROY message is sent. Notice that the WM_CLOSE is only a message requesting that you close (like WM_QUIT) - you don't actually have to exit/quit. But the WM_DESTROY message tells you that your window IS being closed and destroyed so you must cleanup any resources, handles etc.
Just so it doesn't get lost in the comments... don't forget about WM_CANCEL. When you click the close (x) button on an MFC dialog, it will certainly send WM_CLOSE. The default OnClose() function will then call the default (base class) OnCancel() function.
However, if you simply type the ESC key, this will lead to the closure of the dialog, but (as far as I can tell) without generating the WM_CLOSE event - it goes directly to the WM_CANCEL/OnCancel() mechanism.
I hereby invite the community to elaborate on this... or edit that elaboration into the accepted answer.
At first let's discuss WM_QUIT - the difference from another messages that this is not associated with window. It is used by application. For example this can be handled by non-visible standalone OLE server (.exe, but not in-proc as .dll)
WM_CLOSE - per msdn: "An application can prompt the user for confirmation, prior to destroying a window" - it is used as notification about intention to close (you can reject this intention).
WM_DESTROY - is a fact that window is closing and all resources must(!) be deallocated.
I know this is old, but just trying to provide a clearer answer for anyone.
// What causes each message?
WM_CLOSE: // Pressed Close Button (X) / Alt+F4 / "Close" in context menu
WM_DESTROY: // Called DestroyWindow(hwnd)
WM_QUIT: // Called PostQuitMessage(exit)
// What do they do by default?
case WM_CLOSE: DestroyWindow(hwnd); return 0; // pressed close? destroy window.
case WM_DESTROY: PostQuitMessage(0); return 0; // destroyed window? quit message loop.
// WM_QUIT isn't associated with a window, so isn't sent to the window procedure
So WM_CLOSE is just so we can request to destroy the window. However you might want it to show a popup in your game asking if you're sure. (and if you want to save first)
And WM_DESTROY doesn't actually post the quit message by default, since you could have multiple windows. That's just the usual event we quit after. You could have WM_CLOSE call PostQuitMessage(exit) and destroy your window(s) after the message loop if you wanted.
If you wanted a custom close button in your game, it should do what WM_CLOSE does.
Also there is the function CloseWindow(hwnd), however it simply minimizes the window.
Hope this helps anyone.
Theres some reason for this code not reach the first else?
I got it exactly the same from vairous sources. Than I did my own encapsulation. Everything goes fine. Window is created, messages are treated, events are generated to keyborad input in the client area, the gl canvas works fine (when I force it to draw).
The only problem is that message loop never leaves the first if. :/
I'm really stuck.
while (!done)
{
if (::PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
done = TRUE;
}
else
{
::TranslateMessage (&msg);
::DispatchMessage (&msg);
}
}
else
{
// Code is never reaching this!
draw ();
::SwapBuffers(hDC);
idle ();
}
}
return msg.wParam;
In your case the message queue must never be empty - why? Well it depends on what the rest of your program is doing. Some possibilities:
Your code is posting new messages to the queue in a manner such that the queue doesn't get empty. I'd suggest logging out the message ids as they are handled.
You aren't handling paint messages - from msdn:
"The PeekMessage function normally does not remove WM_PAINT messages from the queue. WM_PAINT messages remain in the queue until they are processed. However, if a WM_PAINT message has a NULL update region, PeekMessage does remove it from the queue."
Hope this helps.
[Edit]
To handle WM_PAINT either call BeginPaint and EndPaint or forward to DefWindowProc
Make sure you are processing the WM_PAINT correctly.
By this I mean make sure you are calling BeginPaint and EndPaint from inside the WM_PAINT message, otherwise you will be confusing Windows into thinking your application still needs to be painted.
May be there is always a message waiting ?
PeekMessage will return 0 only if there are no messages in the message queue. Since there are messages to be dispatched in the message queue, it is returning a non-zero value and your else condition is never executed.