Search code examples
wxwidgets

wxPanel flickering/failure when window is inactive


Basically, I have a wxWidget application that implements OpenGL - the latter is displayed on a panel that can be updated through user input (clicking, dragging, etc), wxTimer, or events generated by external processes. The problem arises when focus is shifted to another window (whether an internal dialog box, or another application entirely) - the wxPanel ceases to update anywhere from immediately to after a few seconds, particularly if the other window is on top of it (sometimes, a small part of the panel that was obscured will still continue to update). Reactivating the application or resizing the window "unfreezes" the panel, and normal operation continues.

This is an issue I've always had in wxWidgets, be it with an OpenGL panel such as in this case, or otherwise. Generally, I've been able to get around it by making numerous SwapBuffer() calls in between a Freeze() and a Thaw() upon window refocusing, window resizing, or something similarly kludgy, but these all have the potential to produce flicker or other non-negligible visual artifacts, and additionally can affect performance if done every frame (such as when an animation needs to continue playing in an inactive window).

An indeterminate period of trial and error could probably produce something nice and kludgy for this also, but I'd really like to know, what is the "right" way to handle this problem? Many thanks in advance.

Here's a skeleton of the code for reference:

void MyGLCanvas::Draw(bool focus, int parentID) //This is what's called by the rest of the application
{
    if (focus) { SetFocus(); }  
    SetCurrent();
    wxClientDC dc(this);
    Paint(dc);
}
void MyGLCanvas::Paint(wxDC &dc)
{
    //All OpenGL drawing code here
    glFlush();
    SwapBuffers();
}
void MyGLCanvas::OnPaint(wxPaintEvent& event)
{
    wxPaintDC dc(this);
    Paint(dc);
    event.Skip();
}

Solution

  • You're doing several strange or downright wrong things here:

    1. Do not skip the event in your OnPaint(). This is a fatal error in wxWidgets 2.8 under Windows and even though we clean up after your code if it does this in 3.0, it's still a bad idea.
    2. Don't use wxClientDC, just call Refresh() and let Windows repaint the window by invoking your existing OnPaint() handler.
    3. Don't call SetFocus() from Draw(), this really shouldn't be necessary.
    4. Do call SetCurrent() before drawing. Always, not just in Draw().

    I don't know which of those results in the problems you're seeing but you really should change all of them. See samples\opengl\cube\cube.cpp for an example of how to do it correctly.