[haiku-appserver] Re: [Haiku-commits] r12082 - haiku/trunk/src/kits/interface

  • From: Michael Lotz <mmlr@xxxxxxxx>
  • To: haiku-appserver@xxxxxxxxxxxxx
  • Date: Tue, 29 Mar 2005 10:24:24 +0200

 > For B_KEY_DOWN, BWindow will first handle this message in
 > DispatchMessage to intercept TAB, SHIFT+TAB, OPTION+TAB, etc keystrokes
 > to that it would advance to the next focus item/group. It is after that
 > check that it calls fFocus->KeyDown(); Same goes for the menu and
 > DefaultButton.

This is exactly what has to be avoided here. For clarifying what I mean, here's 
an example 
situation (from the MiniTerminal actually):

You have your BWindow which has a BView attached where you track B_KEY_DOWNs 
with the 
KeyDown hook function. This works well for almost all cases. But if you want to 
write a 
Terminal application, you will need to get those B_TAB keys too.

The BeBook describes the BMessageFilter as follows:

A BMessageFilter is a message-screening function that you "attach" to a BLooper 
or 
BHandler. The message filter sees messages just before they're dispatched (i.e. 
just 
before BLooper::DispatchMessage()), ...

So the way we can get those B_TAB keys is to attach a 
BMessageFilter(B_KEY_DOWN, <filter 
function>) to the BView. We add it to the view because if we had multiple views 
we maybe 
would want a different function to handle the B_TAB for any given view 
depending on which 
one has the focus.
If you follow the BeBook all B_KEY_DOWN messages that would arrive in the 
KeyDown hook 
later should go to the filter function, plus those who would normally be 
dispatched by 
BWindow like the B_TAB.
Look at MiniView.cpp in src/tests/apps/miniterminal for the implementation. 
This way it 
works under R5 as it should and we get the following if we hit debugger from 
the filter 
function:

w>MiniTerminal:sc
    frame         retaddr
fd039c18   8000608e  MiniView::MessageFilter(BMessage *, BHandler **, 
BMessageFilter *) + 
0000002e
fd039c58   ec199012  BLooper::apply_filters(BList *, BMessage *, BHandler *) + 
000000e2
fd039c88   ec19a8ab  BLooper::top_level_filter(BMessage *, BHandler *) + 
00000087
fd039cac   ec25307e  BWindow::task_looper(void) + 000003a2
fd039d28   ec19ae46  BLooper::_task0_(void *) + 00000036
fd039d3c   ec06c50d  thread_start + 00000039
w>MiniTerminal:

This tells us, the BWindow::task_looper got the message, called apply_filters 
and found 
the filter we added to our BView and called it's function.
Without my change, we do pretty much the same thing, we would get into the 
task_looper and 
would call apply_filters. BUT we would not catch the filter that was added to 
our BView, 
because we would only check the filters that were applied to the BWindow itself 
and not to 
the view that will later receive the B_KEY_DOWN event.
If we bdb the MiniTerminal from where we will find that the 
BWindow::task_looper calls 
BWindow::determine_target somewhere before applying the filters. What I guess 
from this 
now is: The BWindow finds out whether itself or the focus view will be the 
target for this 
event and then calls apply_filters for the actual target and not itself.
For any B_KEY_DOWN or B_KEY_UP event aswell as B_UNMAPPED_KEY_DOWN, 
B_UNMAPPED_KEY_UP, 
B_MODIFIERS_CHANGED and B_MOUSE_WHEEL_CHANGED the focus view will be the actual 
target, so 
this is what determine_target returns.

This does not break the path of the message to dispatch. Even if we change the 
target, the 
BWindow will get the message to dispatch, but only if we did not filter it out 
by our 
BMessageFilter:

if (handler) // the determine_target one -> our MiniView
{
        //      Do filtering
        handler = top_level_filter(fLastMessage, handler);
                // filtering is applied to the BMessageFilters of MiniView
        
        // if we skip the message from within the filter, handler will be NULL.
        // if we don't change the handler to something else from within our
        // filter funcation, the looper of the handler will be the looper of
        // the BWindow we are in -> this one.
        if (handler && handler->Looper() == this)
        {
                // so we get here normally and can handle B_TAB from
                // BWindow for TAB navigation.
                DispatchMessage(fLastMessage, handler);
        }
}

We must find out what the target will be first, only then we can call the 
correct filters.
I hope this clears it up a bit.

Regards
Michael Lotz

Other related posts: