[haiku-gsoc] Re: haiku-gsoc Digest V4 #22

  • From: Stephan Aßmus <superstippi@xxxxxx>
  • To: haiku-gsoc@xxxxxxxxxxxxx
  • Date: Tue, 28 Jun 2011 09:44:56 +0200

Hi Nathan,

On 28.06.2011 08:16, Nathan Heisey wrote:
Thanks for your advice.  Patch 01 was premature; most of the
modifications introduced are going under heavy review and redesign at
the moment, based upon the x11/windows code, as suggested.  I will keep
the rest of your advice in mind as well as I become ready to deal with
it.  Currently I am writing the functions that generate events; I will
redesign the event handling once I have made significant progress there.
I won't be correcting this patch since I need to rewrite most of that
anyway.  The next patch I release will probably contain corrections to
the windowing code; whether it contains your suggested corrections to
the event sending/handling is currently up for grabs.  Don't expect it
for a while, though.

I would like to urge you to go with my messaging advice right from the start. It is a waste of time to keep on going with something that you already know today how it would need to be written for real. To demonstrate what I was talking about with some real code (and to show you how little extra code it is), let's have a look at how the minimize event would work without threading issues:

Defined in a common header, you have a message constant for each type of SDL event that can be raised:

enum {
    MSG_WINDOW_EVENT    = 'wdev',

    // More event message constants for other type of events...
}


In your SDL BWindow implementation:

virtual void Minimize(bool minimize)
{
    if (minimize)
        _ForwardWindowEvent(SDL_WINDOWEVENT_MINIMIZED);
    else
        _ForwardWindowEvent(SDL_WINDOWEVENT_RESTORED);

    BWindow::Minimize(minimize);
}


The actual forwarding happens in a private method, like so:

void _ForwardWindowEvent(int32 sdlEventConstant)
{
    BMessage message(MSG_WINDOW_EVENT);
    message.AddPointer("window", window);
    message.AddInt32("event", sdlEventConstant);
    be_app->PostMessage(&message);
}


In your BApplication implementation, you would handle this event like so:

virtual void MessageReceived(BMessage* message)
{
    switch (message->what) {
        case MSG_WINDOW_EVENT:
            _HandleWindowEvent(message);
            break;

        // Lots of other cases...

        default:
            BApplication::MessageReceived(message);
            break;
    }
}

(private)
void _HandleWindowEvent(BMessage* message)
{
    WhateverTypeWindowIs* window;
    if (message->FindPointer("window", (void**)&window) != B_OK)
        return;

    int32 sdlEventConstant;
    if (message->FindInt32("event", &sdlEventConstant) != B_OK)
        return;

    SDL_SendWindowEvent(window, sdlEventConstant, 0, 0);
}

As you can see, this is only a little extra code, but it is really necessary to deal with this properly. While you already write everything from scratch, you need to write it like this, or the SDL events will simply be called on the wrong thread.

If you do all this with a global lock, the extra code could be smaller, but you would have the problem that while there are no "real" threading issues because of the locking, you do invoke functions on the wrong thread (with a different thread priority, too). Debugging code in WebKit for example specifically checks for this condition and raises an assertion (assuming locking issues), SDL may already have similar debugging facilities, or may introduce them in the future. With the message forwarding approach above, you make sure that you invoke SDL functions indeed on the expected thread (the one running main()).

One more thing: It is important that your SDL BWindow does not go away before the BApplication has a chance to process pending window events, otherwise it creates an SDL window event with a window pointer that is stale. To achieve this, simply don't quit the BWindow from the BWindow thread, like so (in your BWindow implementation):

virtual bool QuitRequested()
{
    _ForwardWindowEvent(SDL_WINDOWEVENT_CLOSE);
    // Let the BApplication deal with this, we never quit outselves.
    return false;
}

_HandleWindowEvent() in BApplication could be extended like so:

void _HandleWindowEvent(BMessage* message)
{
    TypeOfWindow* window;
    if (message->FindPointer("window", (void**)&window) != B_OK)
        return;

    int32 sdlEventConstant;
    if (message->FindInt32("event", &sdlEventConstant) != B_OK)
        return;

    SDL_SendWindowEvent(window, sdlEventConstant, 0, 0);

    if (sdlEventConstant == SDL_WINDOWEVENT_CLOSE) {
        // Do whatever is necessary to close the BWindow that
        // represents "window". Find "bWindow" in some HashMap...
        // if (bWindow->Lock())
        //     bWindow->Quit();
    }
}

I could be off on this, perhaps SDL_WINDOWEVENT_CLOSE already triggers the right sequence of events inside SDL (asking the app for unsaved changes, yada yada). It's just important to be aware that the BApplication handles all these events asynchronously and you need to make sure the BWindow is still around right then.

Best regards,
-Stephan

Other related posts: