Page 1 of 1

#PB_Event_Repaint getting lost?

Posted: Sun Mar 01, 2009 7:13 pm
by maker
Consider this code:

Code: Select all

; This program illustrates why I am reluctant to use
; PureBasic'S built-in window management; it doesn't update
; events quite right (move the window off screen then back,
; it won't repaint until you release the mouse button).
;
Global appName.s = "HelloWin", windowTitle.s = "A Hello Program"

Global clientAreaText.s = "Hello, Windows!"

Define mainwin = OpenWindow(#PB_Any, #PB_Ignore, #PB_Ignore, 100, 100, windowTitle, #WS_OVERLAPPEDWINDOW | #PB_Window_Invisible)
    SetWindowColor(mainwin, GetSysColor_(#COLOR_WINDOW))
HideWindow(mainwin,#FALSE)

If mainwin
    Repeat
        Define event = WindowEvent()
        If event
            If #PB_Event_Repaint
                If StartDrawing(WindowOutput(mainwin))
                    DrawText(0,0,clientAreaText)
                    StopDrawing()
                EndIf
            EndIf
        Else
            Delay(1)
        EndIf
    Until event = #PB_Event_CloseWindow
EndIf

End 0

So, is this a bug or an intentional limitation, i.e. a tradeoff that is accepted in exchange for other benefits?

Posted: Sun Mar 01, 2009 7:19 pm
by freak
> So, is this a bug or an intentional limitation, i.e. a tradeoff that is accepted in exchange for other benefits?

Its because Windows doesn't return control to the message loop while the window is being moved. It basically all happens inside a single (Wait-)WindowEvent() call. You can get these events in a window callback though.

Posted: Sun Mar 01, 2009 8:32 pm
by maker
freak wrote: Its because Windows doesn't return control to the message loop while the window is being moved. It basically all happens inside a single (Wait-)WindowEvent() call. You can get these events in a window callback though.
Yes, I've done a lot of API coding, both in other languages and with the trial version of PB before I registered. That's why I noticed the odd behaviour; API coding doesn't have that limitation.

However, I'm puzzled by your phrasing: "Windows doesn't return control to the message loop while the window is being moved." Did you mean that PureBasic doesn't return control, or perhaps some C++ routine in the background doesn't return control? Because Windows itself, as noted by both of us above, does not block access to these events.

Posted: Sun Mar 01, 2009 9:17 pm
by freak
maker wrote:Yes, I've done a lot of API coding, both in other languages and with the trial version of PB before I registered. That's why I noticed the odd behaviour; API coding doesn't have that limitation.
Its not a question of API or no API. Its a question about where you process the events. As i said, the message loop will not see these events, but callbacks will. You can replace the WaitWindowEvent() function with the GetMessage() / PeekMessage() API functions and you will not get them either.

If DefWindowProc() (the default Windows message handler) handles the message that initiates the move, it will not return until the move is done. Messages are only posted to the window callbacks but no longer to the message queue during this time. This is the Windows behavior, its not PureBasic specific.

Posted: Mon Mar 02, 2009 2:11 am
by maker
Bear with me, I think I'm getting closer to understanding...
freak wrote:If DefWindowProc() (the default Windows message handler) handles the message that initiates the move, it will not return until the move is done. Messages are only posted to the window callbacks but no longer to the message queue during this time. This is the Windows behavior, its not PureBasic specific.
That's the part that I still don't get. If DefWindowProc_() is not returning until the move is done, then the code below should have the same problem, but it doesn't...which seems to indicate that PB's default window handler is doing something different, or that I am misunderstanding your explanation.

Code: Select all

Global appName.s = "AppName", windowTitle.s = "The Window Text"

Global instance = GetModuleHandle_(#NULL)

Procedure winHandler(hwin, message, wParam, lParam)
    Static clientWidth, clientHeight
    Define hdc

    Select message
        Case #WM_SIZE
            clientWidth = lParam & $FFFF
            clientHeight = (lParam >> 16) & $FFFF
        Case #WM_PAINT
            Define ps.PAINTSTRUCT, rc.RECT
            hdc = BeginPaint_(hwin, ps)

            GetClientRect_(hwin, rc)
            DrawText_(hdc, "Hello, Windows!", -1, rc, #DT_SINGLELINE | #DT_CENTER | #DT_VCENTER)

            EndPaint_(hwin, ps)
        Case #WM_DESTROY
            PostQuitMessage_(0)
        Default
            ProcedureReturn DefWindowProc_(hwin, message, wParam, lParam)
    EndSelect

    ProcedureReturn 0
EndProcedure

Procedure main()
    Define myWndClass.WNDCLASS

    myWndClass\style = #CS_HREDRAW | #CS_VREDRAW
    myWndClass\lpfnWndProc =  @winHandler()
    myWndClass\hInstance = instance
    myWndClass\hIcon = LoadIcon_(0, #IDI_APPLICATION)
    myWndClass\hCursor = LoadCursor_(0, #IDC_ARROW)
    myWndClass\hbrBackground = #COLOR_WINDOW + 1
    myWndClass\lpszClassName = @appName

    If Not RegisterClass_(myWndClass)
        MessageBox_(#NULL, "Cannot register window class!", appName, #MB_ICONERROR)
        ProcedureReturn 0
    EndIf

    Define hwin = CreateWindowEx_(#NULL, appName, windowTitle, #WS_OVERLAPPEDWINDOW, #CW_USEDEFAULT, #CW_USEDEFAULT, #CW_USEDEFAULT, #CW_USEDEFAULT, #NULL, #NULL, instance, #NULL)
    If Not hwin
        MessageBox_(#NULL, "Unable to create main window.", appName, #MB_ICONERROR)
        ProcedureReturn 0
    EndIf

    ShowWindow_(hwin, #SW_SHOWDEFAULT)
    UpdateWindow_(hwin)

    Define msg.MSG
    While GetMessage_(msg, #NULL, 0, 0) > 0
        TranslateMessage_(msg)
        DispatchMessage_(msg)
    Wend

    ProcedureReturn msg\wParam
EndProcedure

End main()

Posted: Mon Mar 02, 2009 2:32 am
by freak
As i said, the callbacks are not the problem. You can get all events with SetWindowCallback() in PB too.

Its the event loop (your GetMessage_() loop) which will not get these events, because it does not run while the move is in progress. (Wait-)WindowEvent() basically implements this event loop, thats why it cannot get the paint messages.

Posted: Mon Mar 02, 2009 4:18 am
by maker
freak wrote:As i said, the callbacks are not the problem. You can get all events with SetWindowCallback() in PB too.

Its the event loop (your GetMessage_() loop) which will not get these events, because it does not run while the move is in progress. (Wait-)WindowEvent() basically implements this event loop, thats why it cannot get the paint messages.
I think I am not phrasing something right. Again, bear with me and let me try it this way:

In 'normal' pb code, the problem I first pointed out exists.

In my lower-level (i.e. traditional C-ish) approach, the problem does not exist.

I understand that WindowEvent() will not get the #PB_Event_Repaint message until the move is over.

I understand that GetMessage_() will not return anything until the move is over.


In order for the window to exist at all, there has to be a callback. In the case of a window opened with OpenWindow(), the callback is behind the scenes, supplied by PB (unless I assign one, which I did not in the first example).

Since code using my callback (second code sample) does not cause the paint problem, but the built-in-callback code (first code sample) does have the problem, it follows logically that the behind-the-scenes callback is doing something differently.

And that's what I'm curious about: what is the 'something different'? It can't be an inherent limitation in Windows itself, otherwise all programs would have exactly that same limitation. So, again, it has to be something different in the way PB is handling things.

Woops; I think I may have just figured it out, or at least I've thought of a possible explanation. See how this sounds:

GetMessage() loops never get the WM_PAINT message, it goes straight to the window handler (callback). So for WindowEvent() to be able to return the #PB_Event_Repaint at all, it must be getting 'signalled' by the built-in callback when the callback receives a WM_PAINT message. But since the GetMessage() loop inside WindowEvent() doesn't run until the window move is over, it can't do anything about it.

If this is the case, then I can answer my original question: it's not a bug, but it is a limitation inherent in the way PB chooses to handle such things, not Windows itself. It's not the WindowEvent() routine alone, it's not the built-in callback alone, it's the interaction between the two, and it's pretty much unavoidable with this structure (not that I know of any better ones; everything I can think of that would offer a finer degree of control boils down to custom callbacks under different guises; for instance, the endless 'event routines' in old Visual Basic).

Thanks for your patience, freak! All this back-and-forth helped me figure it out. err, maybe. :D