Be aware, forwardMessageToParent() could cause WM_CHAR to come twice

Hey JUCE community,

I want to share some observation about how WM_KEYDOWN, WM_CHAR and WM_KEYUP messages are handled. Imagine window layout where you have a top level window and a child window representing content of that top level window. All key command are handled in the top level window. Let’s look on message processing code:

    LRESULT peerWindowProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam)
    {
        switch (message)
        {
       	......
            case WM_KEYDOWN:
            case WM_SYSKEYDOWN:
                if (doKeyDown (wParam))
                    return 0;

                forwardMessageToParent (message, wParam, lParam);
                break;

            case WM_KEYUP:
            case WM_SYSKEYUP:
                if (doKeyUp (wParam))
                    return 0;

                forwardMessageToParent (message, wParam, lParam);
                break;

            case WM_CHAR:
                if (doKeyChar ((int) wParam, lParam))
                    return 0;

                forwardMessageToParent (message, wParam, lParam);
                break;
        ......
            default:
                break;
        }

        return DefWindowProcW (h, message, wParam, lParam);
    }

Let’s discuses how keyboard input is processed. In our imaginative case it starts from WM_KEYDOWN message coming to child “content” window. If message is not handled with doKeyDown (), which is the case, it is forwarded to parent window and meanwhile processed by default message processing function DefWindowProc (). It this point let’s one more bit to our imaginative case. let’s assume we hit space to start playback. In that case default message processor translates “key down” message in to a corresponding WM_CHAR message and sends it to the child window. So now we get WM_CHAR message, it is also not processed by a child “content” window and simply forwarded to a parent window. Then we get WM_KEYUP, which also, just passed to parent.

All forwarded messages are coming to parent window. in our particular case, top level window, handles only doKeyChar () while skipping doKeyDown () and doKeyUp (). Messages are coming in the same sequence. WM_KEYDOWN message is not handled and end up passing to default message processor. The same as in case with child window, DefWindowProc () generates new WM_CHAR message, which is sent to a top level window. Then coming WM_CHAR message forwarded from child window, and later comes WM_KEYUP message.

As you can see WM_CHAR message comes two times to parent window. In discussed case it leads to playback being stopped immediately after it is started. Of course that is happening only with WM_KEYDOWN messages which are translate to WM_CHAR by default message processor. The simplest way to resolve this problem is to remove call to forwardMessageToParent () for WM_CHAR handling case.

I hope this information is useful and I hope Juce developers support me and add this change to framework code. I would also be very happy to discuss other option to solve the issue!