Jump to content
  • Advertisement
Sign in to follow this  
Trillian

[Win32] Yet another unresponsive window

This topic is 4283 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello! I've just completed the windowing part of my engine and now, as every time I did a windowing part of some engine, I get an unresponsive window. The window shows up and renders (using ogl) correctly. It processes some window messages (well, some messages go through it's procedure), but it never receives a WM_SIZE, WM_MOVE, WM_CLOSE but I do receive WM_ACTIVATE messages. I cannot drag around or resize the window, neither can I close it using the [X] button. My window style is as follows : DWORD style = WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MAXIMIZEBOX; Any ideas on what might be causing this?

Share this post


Link to post
Share on other sites
Advertisement
Most likely you have some blocking code somewhere that is not allowing the program to continue. The easiest thing to do is just break into the code and see where it is hanging out, most likely it will break in the same spot every time.

theTroll

Share this post


Link to post
Share on other sites
The main loop runs normally and the window is updated (translate and dispatch messages) at each iteration. Nothing breaks the loop (If I set a breakpoint, it loops to it normally).

However, I've noticed, and I don't know if it is normal, that when I pause the application while it's running, it always pauses at this line :

virtual CRYO_bool flipBuffers() { return ::SwapBuffers(m_DC) == TRUE; }

I don't know if this means anything because ::SwapBuffers does return TRUE, but it still seems strange to me.

I've simplified the main loops to it's minimum, now it runs the following in a tight loop :
CRYO_void Win32Context::update_impl()
{
MSG msg;
while(::PeekMessage(&msg, m_Window, 0, 0, PM_REMOVE))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}

... But the window still acts the same way (however now the backbuffer isn't cleared now so we "see through the window").

Any other options?

Share this post


Link to post
Share on other sites
In PeekMessage(), unless you have multiple windows and you explicitly want to only handle messages for the one represented by m_Window, replace m_Window with NULL. It's not necessary to filter window messages by HWND unless you really mean to.

Can we see your window procedure?

Share this post


Link to post
Share on other sites
You have to pass on all messages to DefWindowProc unless you completely handle them yourself.

There is NO valid return code for all unhandled messages. The following snippet is dead wrong:


LRESULT WINAPI MyWindowProc( ... )
{

switch ( message )
{
case WM_MOUSEMOVE:
// do something
break;
default:
return DefWindowProc( .. )
}
return 0;

}


Move the DefWindowProc outside the switch. Keeping it inside default you can't handle the message yourself AND pass it on (unless you copy the DefWindowProc call).

Change it to something like this:


LRESULT WINAPI MyWindowProc( ... )
{

switch ( message )
{
case WM_MOUSEMOVE:
// do something
break;
}
return DefWindowProc( .. )
}


May the fool that put up the tutorial with the DefWindowProc call inside the default case be sporked in the face. Repeatedly. (SarcasmTM)

Share this post


Link to post
Share on other sites
Quote:
Original post by Endurion
You have to pass on all messages to DefWindowProc unless you completely handle them yourself.
...snip...


I prefer the following:

LRESULT WINAPI MyWindowProc( ... )
{
LRESULT result=0;
bool call_default_proc=false;

switch ( message )
{
case WM_MOUSEMOVE:

// do something
result = //some value
break;

case WM_some other message
// do something else
call_default_proc=true;
break;

default:
call_default_proc=true;
break;
}

return call_default_proc ? DefWindowProc (...) : result;
}


Skizz

Share this post


Link to post
Share on other sites
Quote:
Original post by Skizz
I prefer the following:

...
case WM_MOUSEMOVE:

// do something
result = //some value
break;

case WM_some other message
// do something else
call_default_proc=true;
break;
...
return call_default_proc ? DefWindowProc (...) : result;


Skizz


Why not simply place the return inside the case statement for WM_MOUSEMOVE?


...
case WM_MOUSEMOVE:

// do something
return //some value

case WM_some other message
// do something else
break;
...
return DefWindowProc (...);

Share this post


Link to post
Share on other sites
Mainly because I like "single entry point, single exit point" code. There's nothing wrong with using multiple return like that. Also I usually use a map rather than a switch:

LRESULT Proc (...)
{
args.use_default = false;
args.result = 0;
args.w_param = w_param;
args.l_param = l_param;

if (map has message)
{
call map function (args);
}
else
{
args.use_default = true;
}

result args.use_default ? DefWindowProc (...) : args.result;
}


Skizz

Share this post


Link to post
Share on other sites
Okay, I'm back. Thanks for your feedback. I will post my window procedure code.

There's only one thing that I should mention : The WNDCLASS structure is filled with ::DefWindowProc as the window procedure. HOWEVER, after window creation, this is changed to my own window procedure like this :

::SetWindowLongPtr(in_window, GWL_WNDPROC, (LONG_PTR)(void*)StaticWindowProc);
::SetWindowLongPtr(in_window, GWL_USERDATA, (LONG_PTR)this);

This change works because my window precedure does receive messages, it just doesn't receive all of them. E.g. I receive WM_ACTIVATE but not WM_MOVE or WM_SIZE. The actual window procedure is as follows :


LRESULT CALLBACK Win32Context::StaticWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
// Change this to use std::map thing!
Win32Context * context = (Win32Context*)::GetWindowLongPtr(hwnd, GWL_USERDATA);

if(context != NULL && context->m_Window == hwnd)
{
return context->WindowProc(hwnd, msg, wparam, lparam);
}

return ::DefWindowProc(hwnd, msg, wparam, lparam);
}

LRESULT CALLBACK Win32Context::WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
CRYO_bool forwardtodefwindowproc = CRYO_true;

switch(msg)
{
// If the window gets activated or deactivated
case WM_ACTIVATE:
{
if(!HIWORD(wparam))
{
for(CRYO_uint i = 0 ; i < m_Listeners.size() ; ++i)
{
m_Listeners->context_onActivate(*this);
}
}
else
{
for(CRYO_uint i = 0 ; i < m_Listeners.size() ; ++i)
{
m_Listeners->context_onDeactivate(*this);
}
}

forwardtodefwindowproc = CRYO_false;
break;
}

// This makes the screensaver unable to boot
case WM_SYSCOMMAND:
{
forwardtodefwindowproc = CRYO_false;
break;
}

// If we are told to close
case WM_CLOSE:
{
for(CRYO_uint i = 0 ; i < m_Listeners.size() ; ++i)
{
m_Listeners->context_onClose(*this);
}

forwardtodefwindowproc = CRYO_true;
break;
}

// If the window is resized
case WM_SIZE:
{
RECT temp;
::GetClientRect(hwnd, &temp);
Size newsize(temp.right - temp.left, temp.bottom - temp.top);
for(CRYO_uint i = 0 ; i < m_Listeners.size() ; ++i)
{
m_Listeners->context_onResize(*this, newsize);
}

// Make renderer recompute it's projection type
Render::ProjectionType projtype = m_Renderer->getProjType();
m_Renderer->setProjType(Render::PROJTYPE_IDENTITY);
m_Renderer->setProjType(projtype);

forwardtodefwindowproc = CRYO_true;
break;
}

// If the window is moved
case WM_MOVE:
{
Point newposition(LOWORD(lparam), HIWORD(lparam));
for(CRYO_uint i = 0 ; i < m_Listeners.size() ; ++i)
{
m_Listeners->context_onMove(*this, newposition);
}

forwardtodefwindowproc = CRYO_true;
break;
}

// If the window is shown or hid
case WM_SHOWWINDOW:
{
if(wparam == TRUE)
{
for(CRYO_uint i = 0 ; i < m_Listeners.size() ; ++i)
{
m_Listeners->context_onShow(*this);
}
}
else
{
for(CRYO_uint i = 0 ; i < m_Listeners.size() ; ++i)
{
m_Listeners->context_onHide(*this);
}
}

forwardtodefwindowproc = CRYO_true;
break;
}
}

if(forwardtodefwindowproc) return ::DefWindowProc(hwnd, msg, wparam, lparam);
return 0;
}



But I don't think the problem is in there because I don't receive WM_MOVE and WM_SIZE messages at all, they don't go through my procedure.

Any ideas?

Share this post


Link to post
Share on other sites
That is a very odd way to specify the window procedure when creating a window. You're effectively sub-classing the window. Normally, to do what you're trying to do, you'd specify the 'this' pointer as the lpParam parameter to CreateWindowEx. This is then passed to the window procedure as the lpCreateParams value in the CREATESTRUCT pointed to by the lParam of the WM_CREATE/WM_NCCREATE messages.

class WindowWrapper
{
WindowWrapper ()
{
WNCLASSEX window_class;
window_class.lpfbWndProc = WProc; // need to cast correctly
// set up other window_class members
RegisterClassEx (&window_class);
m_window = CreateWindowEx (...., reinterpret_cast <LPVOID> (this));
}

static LRESULT WProc (HWND window, UINT message, WPARAM wparam, LPARAM lparam)
{
LRESULT result = 0;
bool use_default = false;

if (message == WM_CREATE)
{
CREATESTRUCT *create = reinterpret_cast <CREATESTRUCT *> (lparam);
SetWindowLongPtr (window, GWL_USERDATA, create->lpCreateParam);
}

WindowWrapper *me = cast <> (GetWindowLongPtr (window, GWL_USERDATA));

if (me)
{
me->ProcessMessage (message, wparam, lparam, result, use_default);
}
else
{
use_default = true;
}
return use_default ? DefWindowProc () : result;
}

void ProcessMessage (UINT message, WPARAM wparam, LPARAM lparam, LRESULT &result, bool &use_default)
{
switch (message) // better to use a map I think
{
// handlers
}
}

HWND m_window;
};


Skizz

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!