Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Win32: My Application freezes while the user resizes the window

I write a win32 application. I implemented the message loop myself like this:

     bool programcontinue = true;
     while(programcontinue)
     {
              while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
              {
                       TranslateMessage(&Msg);
                       DispatchMessage(&Msg);
              }

              IdleProcess();
     }

There is a resizable window in my application. Usually, IdleProcess() gets called several times per second. When the user grabs a corner or an edge of the resizable window, IdleProcess() doesn't get called anymore until the user releases the mouse button.

What happens here?

I tried exchanging the inner while with an if, but that doesn't change the behaviour. It seems like when resizing starts, the handler for that message does not return until the resizing is done?

Is there a way to change this and call IdleProcess() during resizing a few times per second?

Thanks Marc

EDIT:

What I mean by replacing the inner while with if is:

 bool programcontinue = true;
 while(programcontinue)
 {
          if (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))  // <<<<
          {
                   TranslateMessage(&Msg);
                   DispatchMessage(&Msg);
          }

          IdleProcess();
 }

My window Proc is a bit lengthy, but I get the same behavior with a small test app. This is identical to the wndproc the VS Project Wizard creates:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;

    switch (message)
    {
    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        // Parse the menu selections:
        switch (wmId)
        {
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        // TODO: Add any drawing code here...
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
like image 281
marc40000 Avatar asked Jun 23 '10 13:06

marc40000


2 Answers

There are a number of modal operations that happen on windows. Win32 Modal operations refer to functions that put an application into a "mode" by starting their own event processing loop until the mode finishes. Common application modes include drag and drop operations, move/size operations, anytime a dialog pops up that needs input before the application can continue.

So what is happening is: Your message loop is NOT being run. Your window recieved a WM_LBUTTONDOWN message that you passed to DefWindowProc. DefWindowProc determined that the user was trying to size or move the window interactively and entered a sizing/moving modal function. This function is in a message processing loop watching for mouse messages so that It can intercept them to provide the interactive sizing experience, and will only exit when the sizing operation completes - typically by the user releasing the held button, or by pressing escape.

You get notified of this - DefWindowProc sends a WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE messages as it enters and exits the modal event processing loop.

To continue to generate "idle" messages, typically create a timer (SetTimer) before calling a modal function - or when getting a message that DefWindowProc is entering a modal function - the modal loop will continue to dispatch WM_TIMER messages... and call the idle proc from the timer message handler. Destroy the timer when the modal function returns.

like image 79
Chris Becke Avatar answered Nov 08 '22 13:11

Chris Becke


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/

like image 35
nfries88 Avatar answered Nov 08 '22 12:11

nfries88