From f89b4c9a23a12a1bae426cc920dccfb6b0d022db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Tue, 29 Nov 2005 16:01:41 +0000 Subject: [PATCH] Have I said input event handling is done? * didn't realize that mouse events always go to the view under the mouse, not only if its the focus window (FFM can really do harm, after all :-)). * removed a TODO from the list: EventDispatcher::Target is now a public class EventTarget, and every ServerWindow has one. * as a result, EventDispatcher no longer manages targets itself, it just maintains a list of them. You no longer set messengers, you only set targets. * customization of the message filters, they no longer inherit from BMessageFilter (but EventFilter). * a message target is no longer set explicetly anywhere, it's only changed in the message filters if needed. * therefore, no more locking mess in the EventDispatcher needed. * this also made the EventDispatcher::fLastFocus stuff superfluous. * moved the RootLayer::MouseEventHandler() into the message filter. * Replaced RootLayer::_ChildAt() with WindowAt(). * WindowLayer now has an idea if it has focus or not, it no longer needs to query the RootLayer for this - maybe we should rename "focus" to "active", though (as far as layers are concerned). * the "_view_token" data is now added from the EventDispatcher, not the (Window)Layer class anymore. * removed Layer::MouseWheelChanged() as we currently don't need it (if the need arises, we can add it back later again) * there is still no mouse moved message sent when opening a window under the cursor, though... git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@15228 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/servers/app/Desktop.cpp | 145 +++++++++--- src/servers/app/EventDispatcher.cpp | 346 ++++++++-------------------- src/servers/app/EventDispatcher.h | 82 ++++--- src/servers/app/Layer.cpp | 15 +- src/servers/app/Layer.h | 7 +- src/servers/app/RootLayer.cpp | 60 ++--- src/servers/app/RootLayer.h | 17 +- src/servers/app/ServerWindow.cpp | 19 +- src/servers/app/ServerWindow.h | 4 + src/servers/app/WindowLayer.cpp | 35 ++- src/servers/app/WindowLayer.h | 11 +- 11 files changed, 342 insertions(+), 399 deletions(-) diff --git a/src/servers/app/Desktop.cpp b/src/servers/app/Desktop.cpp index c1a3b2ae6b..a83df0d94e 100644 --- a/src/servers/app/Desktop.cpp +++ b/src/servers/app/Desktop.cpp @@ -49,26 +49,46 @@ #endif -class KeyboardFilter : public BMessageFilter { +class KeyboardFilter : public EventFilter { public: KeyboardFilter(Desktop* desktop); - virtual filter_result Filter(BMessage* message, BHandler** _target); + virtual filter_result Filter(BMessage* message, EventTarget** _target, + int32* _viewToken); private: - Desktop* fDesktop; + Desktop* fDesktop; + EventTarget* fLastFocus; + bigtime_t fTimestamp; +}; + +class MouseFilter : public EventFilter { + public: + MouseFilter(Desktop* desktop); + + virtual filter_result Filter(BMessage* message, EventTarget** _target, + int32* _viewToken); + + private: + Desktop* fDesktop; }; +// #pragma mark - + + KeyboardFilter::KeyboardFilter(Desktop* desktop) - : BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE), - fDesktop(desktop) + : + fDesktop(desktop), + fLastFocus(NULL), + fTimestamp(0) { } filter_result -KeyboardFilter::Filter(BMessage* message, BHandler** /*_target*/) +KeyboardFilter::Filter(BMessage* message, EventTarget** _target, + int32* /*_viewToken*/) { int32 key; int32 modifiers; @@ -123,6 +143,93 @@ KeyboardFilter::Filter(BMessage* message, BHandler** /*_target*/) return B_SKIP_MESSAGE; } + bigtime_t now = system_time(); + + ::RootLayer* rootLayer = fDesktop->RootLayer(); + rootLayer->Lock(); + + EventTarget* focus = NULL; + if (rootLayer->Focus() != NULL) + focus = &rootLayer->Focus()->Window()->EventTarget(); + + // TODO: this is a try to not steal focus from the current window + // in case you enter some text and a window pops up you haven't + // triggered yourself (like a pop-up window in your browser while + // you're typing a password in another window) - maybe this should + // be done differently, though (using something like B_LOCK_WINDOW_FOCUS) + + if (focus != fLastFocus && now - fTimestamp > 100000) { + // if the time span between the key presses is very short + // we keep our previous focus alive - this is save even + // if the target doesn't exist anymore, as we don't reset + // it, and the event focus passed in is always valid (or NULL) + *_target = focus; + fLastFocus = focus; + } + + rootLayer->Unlock(); + + // we always allow to switch focus after the enter key has pressed + if (key == B_ENTER) + fTimestamp = 0; + else + fTimestamp = now; + + return B_DISPATCH_MESSAGE; +} + + +// #pragma mark - + + +MouseFilter::MouseFilter(Desktop* desktop) + : + fDesktop(desktop) +{ +} + + +filter_result +MouseFilter::Filter(BMessage* message, EventTarget** _target, int32* _viewToken) +{ + BPoint where; + if (message->FindPoint("where", &where) != B_OK) + return B_DISPATCH_MESSAGE; + + ::RootLayer* rootLayer = fDesktop->RootLayer(); + + rootLayer->Lock(); + + WindowLayer* window = rootLayer->MouseEventWindow(); + if (window == NULL) + window = rootLayer->WindowAt(where); + + if (window != NULL) { + // dispatch event in the window layers + switch (message->what) { + case B_MOUSE_DOWN: + window->MouseDown(message, where, _viewToken); + break; + + case B_MOUSE_UP: + window->MouseUp(message, where, _viewToken); + rootLayer->SetMouseEventWindow(NULL); + break; + + case B_MOUSE_MOVED: + window->MouseMoved(message, where, _viewToken); + break; + } + + if (*_viewToken != B_NULL_TOKEN) + *_target = &window->Window()->EventTarget(); + else + *_target = NULL; + } else + *_target = NULL; + + rootLayer->Unlock(); + return B_DISPATCH_MESSAGE; } @@ -185,28 +292,7 @@ Desktop::Init() fEventDispatcher.SetTo(gInputManager->GetStream()); fEventDispatcher.SetHWInterface(fVirtualScreen.HWInterface()); - // temporary hack to get things started - class MouseFilter : public BMessageFilter { - public: - MouseFilter(::RootLayer* layer) - : BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE), - fRootLayer(layer) - { - } - - virtual filter_result - Filter(BMessage* message, BHandler** /*_target*/) - { - fRootLayer->Lock(); - fRootLayer->MouseEventHandler(message); - fRootLayer->Unlock(); - return B_DISPATCH_MESSAGE; - } - - private: - ::RootLayer* fRootLayer; - }; - fEventDispatcher.SetMouseFilter(new MouseFilter(fRootLayer)); + fEventDispatcher.SetMouseFilter(new MouseFilter(this)); fEventDispatcher.SetKeyboardFilter(new KeyboardFilter(this)); // take care of setting the default cursor @@ -560,6 +646,9 @@ Desktop::RemoveWindowLayer(WindowLayer *windowLayer) Unlock(); RootLayer()->RemoveWindowLayer(windowLayer); + + if (windowLayer->Window() != NULL) + EventDispatcher().RemoveTarget(windowLayer->Window()->EventTarget()); } diff --git a/src/servers/app/EventDispatcher.cpp b/src/servers/app/EventDispatcher.cpp index 47ea1ef704..0564d48632 100644 --- a/src/servers/app/EventDispatcher.cpp +++ b/src/servers/app/EventDispatcher.cpp @@ -12,6 +12,8 @@ #include "HWInterface.h" #include "InputManager.h" +#include + #include #include #include @@ -29,11 +31,6 @@ #endif -// TODO: the Target class could be made public, and ServerWindow could inherit -// from it - this would especially be nice if we didn't have to lock the root -// layer when messing with ServerWindow children (because then, RootLayer's -// mouse filter could almost be moved completely to the window). - /*! The differentiation between messenger and token looks odd, but it really has a reason as well: @@ -42,40 +39,7 @@ view(s). */ -class EventDispatcher::Target { - public: - Target(const BMessenger& messenger); - ~Target(); - - BMessenger& Messenger() { return fMessenger; } - - event_listener* FindListener(int32 token, int32* _index = NULL); - bool AddListener(int32 token, uint32 eventMask, uint32 options, - bool temporary); - void RemoveListener(event_listener* listener, bool temporary); - - bool RemoveListener(int32 token); - bool RemoveTemporaryListener(int32 token); - void RemoveTemporaryListeners(); - - bool IsNeeded() const { return fFocusOrLastFocus || !fListeners.IsEmpty(); } - - void SetFocusOrLastFocus(bool focus) { fFocusOrLastFocus = focus; } - bool FocusOrLastFocus() const { return fFocusOrLastFocus; } - - int32 CountListeners() const { return fListeners.CountItems(); } - event_listener* ListenerAt(int32 index) const { return fListeners.ItemAt(index); } - - private: - bool _RemoveTemporaryListener(event_listener* listener, int32 index); - - BObjectList fListeners; - BMessenger fMessenger; - bool fFocusOrLastFocus; - -}; - -struct EventDispatcher::event_listener { +struct event_listener { int32 token; uint32 event_mask; uint32 options; @@ -93,22 +57,27 @@ static const float kStandardImportance = 0.9f; static const float kListenerImportance = 0.8f; -EventDispatcher::Target::Target(const BMessenger& messenger) +EventTarget::EventTarget() : - fListeners(2, true), - fMessenger(messenger), - fFocusOrLastFocus(false) + fListeners(2, true) { } -EventDispatcher::Target::~Target() +EventTarget::~EventTarget() { } -EventDispatcher::event_listener* -EventDispatcher::Target::FindListener(int32 token, int32* _index) +void +EventTarget::SetTo(const BMessenger& messenger) +{ + fMessenger = messenger; +} + + +event_listener* +EventTarget::FindListener(int32 token, int32* _index) { for (int32 i = fListeners.CountItems(); i-- > 0;) { event_listener* listener = fListeners.ItemAt(i); @@ -125,7 +94,7 @@ EventDispatcher::Target::FindListener(int32 token, int32* _index) bool -EventDispatcher::Target::_RemoveTemporaryListener(event_listener* listener, int32 index) +EventTarget::_RemoveTemporaryListener(event_listener* listener, int32 index) { if (listener->event_mask == 0) { // this is only a temporary target @@ -150,7 +119,7 @@ EventDispatcher::Target::_RemoveTemporaryListener(event_listener* listener, int3 void -EventDispatcher::Target::RemoveTemporaryListeners() +EventTarget::RemoveTemporaryListeners() { for (int32 index = CountListeners(); index-- > 0;) { event_listener* listener = ListenerAt(index); @@ -161,7 +130,7 @@ EventDispatcher::Target::RemoveTemporaryListeners() bool -EventDispatcher::Target::RemoveTemporaryListener(int32 token) +EventTarget::RemoveTemporaryListener(int32 token) { int32 index; event_listener* listener = FindListener(token, &index); @@ -173,7 +142,7 @@ EventDispatcher::Target::RemoveTemporaryListener(int32 token) bool -EventDispatcher::Target::RemoveListener(int32 token) +EventTarget::RemoveListener(int32 token) { int32 index; event_listener* listener = FindListener(token, &index); @@ -194,7 +163,7 @@ EventDispatcher::Target::RemoveListener(int32 token) bool -EventDispatcher::Target::AddListener(int32 token, uint32 eventMask, +EventTarget::AddListener(int32 token, uint32 eventMask, uint32 options, bool temporary) { event_listener* listener = new (std::nothrow) event_listener; @@ -231,15 +200,12 @@ EventDispatcher::EventDispatcher() fStream(NULL), fThread(-1), fCursorThread(-1), + fPreviousMouseTarget(NULL), fFocus(NULL), - fLastFocus(NULL), - fTransit(false), fSuspendFocus(false), fMouseFilter(NULL), fKeyboardFilter(NULL), - fTargets(10, true), - // the list owns its items - fListenerLock("listener lock"), + fTargets(10), fCursorLock("cursor loop lock"), fHWInterface(NULL) { @@ -321,101 +287,20 @@ EventDispatcher::_Run() } +/*! + \brief Removes any reference to the target, but doesn't delete it. +*/ void -EventDispatcher::_UnsetLastFocus() -{ - if (fLastFocus == NULL) - return; - - fLastFocus->SetFocusOrLastFocus(false); - if (!fLastFocus->IsNeeded()) - _RemoveTarget(fLastFocus); - - fLastFocus = NULL; -} - - -void -EventDispatcher::SetFocus(const BMessenger* messenger) +EventDispatcher::RemoveTarget(EventTarget& target) { BAutolock _(this); - BAutolock _temp(fListenerLock); - ETRACE(("EventDispatcher::SetFocus(messenger = %p)\n", messenger)); - - if ((messenger == NULL && fFocus == NULL) - || (messenger != NULL && fFocus != NULL && fFocus->Messenger() == *messenger)) - return; - - // update last focus - - if (fLastFocus != NULL) - _UnsetLastFocus(); - - fLastFocus = fFocus; - - // update focus - - if (messenger != NULL) { - fFocus = _FindTarget(*messenger); - if (fFocus == NULL) { - // we need a new target for this focus - fFocus = _AddTarget(*messenger); - if (fFocus == NULL) - printf("EventDispatcher: could not set focus!\n"); - } - if (fFocus != NULL) - fFocus->SetFocusOrLastFocus(true); - } else + if (fFocus == &target) fFocus = NULL; + if (fPreviousMouseTarget == &target) + fPreviousMouseTarget = NULL; - fFocusGotExitTransit = true; - fTransit = true; -} - - -EventDispatcher::Target* -EventDispatcher::_FindTarget(const BMessenger& messenger, int32* _index) -{ - for (int32 i = fTargets.CountItems(); i-- > 0;) { - Target* target = fTargets.ItemAt(i); - - if (target->Messenger() == messenger) { - if (_index) - *_index = i; - return target; - } - } - - return NULL; -} - - -EventDispatcher::Target* -EventDispatcher::_AddTarget(const BMessenger& messenger) -{ - Target* target = new (std::nothrow) Target(messenger); - if (target == NULL) { - printf("EventDispatcher: could not create new target!\n"); - return NULL; - } - - bool success = fTargets.AddItem(target); - if (!success) { - printf("EventDispatcher: could not add new target!\n"); - delete target; - return NULL; - } - - return target; -} - - -void -EventDispatcher::_RemoveTarget(Target* target) -{ - // the list owns its items and will delete the target itself - fTargets.RemoveItem(target); + fTargets.RemoveItem(&target); } @@ -427,19 +312,15 @@ EventDispatcher::_RemoveTarget(Target* target) leaves the event mask untouched and just updates the options. */ bool -EventDispatcher::_AddListener(const BMessenger& messenger, int32 token, +EventDispatcher::_AddListener(EventTarget& target, int32 token, uint32 eventMask, uint32 options, bool temporary) { - BAutolock _(fListenerLock); - Target* target = _FindTarget(messenger); - if (target == NULL) { - // we need a new target for this messenger - target = _AddTarget(messenger); - if (target == NULL) - return false; - } + BAutolock _(this); - event_listener* listener = target->FindListener(token); + if (!fTargets.HasItem(&target)) + fTargets.AddItem(&target); + + event_listener* listener = target.FindListener(token); if (listener != NULL) { // we already have this target, update its event mask if (temporary) { @@ -463,10 +344,10 @@ EventDispatcher::_AddListener(const BMessenger& messenger, int32 token, // we need a new target - bool success = target->AddListener(token, eventMask, options, temporary); + bool success = target.AddListener(token, eventMask, options, temporary); if (!success) { - if (!target->IsNeeded()) - _RemoveTarget(target); + if (target.IsEmpty()) + fTargets.RemoveItem(&target); } else { if (options & B_SUSPEND_VIEW_FOCUS) fSuspendFocus = true; @@ -480,7 +361,7 @@ void EventDispatcher::_RemoveTemporaryListeners() { for (int32 i = fTargets.CountItems(); i-- > 0;) { - Target* target = fTargets.ItemAt(i); + EventTarget* target = fTargets.ItemAt(i); target->RemoveTemporaryListeners(); } @@ -488,61 +369,48 @@ EventDispatcher::_RemoveTemporaryListeners() bool -EventDispatcher::AddListener(const BMessenger& messenger, int32 token, +EventDispatcher::AddListener(EventTarget& target, int32 token, uint32 eventMask, uint32 options) { options &= B_NO_POINTER_HISTORY; // that's currently the only allowed option - return _AddListener(messenger, token, eventMask, options, false); + return _AddListener(target, token, eventMask, options, false); } bool -EventDispatcher::AddTemporaryListener(const BMessenger& messenger, +EventDispatcher::AddTemporaryListener(EventTarget& target, int32 token, uint32 eventMask, uint32 options) { - return _AddListener(messenger, token, eventMask, options, true); + return _AddListener(target, token, eventMask, options, true); } void -EventDispatcher::RemoveListener(const BMessenger& messenger, int32 token) +EventDispatcher::RemoveListener(EventTarget& target, int32 token) { - BAutolock _(fListenerLock); + BAutolock _(this); ETRACE(("events: remove listener token %ld\n", token)); - int32 index; - Target* target = _FindTarget(messenger, &index); - if (target == NULL) - return; - - if (target->RemoveListener(token) && !target->IsNeeded()) { - fTargets.RemoveItemAt(index); - delete target; - } + if (target.RemoveListener(token) && target.IsEmpty()) + fTargets.RemoveItem(&target); } void -EventDispatcher::RemoveTemporaryListener(const BMessenger& messenger, int32 token) +EventDispatcher::RemoveTemporaryListener(EventTarget& target, int32 token) { - BAutolock _(fListenerLock); + BAutolock _(this); + ETRACE(("events: remove temporary listener token %ld\n", token)); - int32 index; - Target* target = _FindTarget(messenger, &index); - if (target == NULL) - return; - - if (target->RemoveTemporaryListener(token) && !target->IsNeeded()) { - fTargets.RemoveItemAt(index); - delete target; - } + if (target.RemoveTemporaryListener(token) && target.IsEmpty()) + fTargets.RemoveItem(&target); } void -EventDispatcher::SetMouseFilter(BMessageFilter* filter) +EventDispatcher::SetMouseFilter(EventFilter* filter) { BAutolock _(this); @@ -555,7 +423,7 @@ EventDispatcher::SetMouseFilter(BMessageFilter* filter) void -EventDispatcher::SetKeyboardFilter(BMessageFilter* filter) +EventDispatcher::SetKeyboardFilter(EventFilter* filter) { BAutolock _(this); @@ -633,13 +501,10 @@ EventDispatcher::_SendMessage(BMessenger& messenger, BMessage* message, bool -EventDispatcher::_AddTokens(BMessage* message, Target* target, uint32 eventMask) +EventDispatcher::_AddTokens(BMessage* message, EventTarget* target, uint32 eventMask) { _RemoveTokens(message); - BAutolock _(fListenerLock); - // temporary lock - int32 count = target->CountListeners(); for (int32 i = count; i-- > 0;) { event_listener* listener = target->ListenerAt(i); @@ -696,7 +561,8 @@ EventDispatcher::_EventLoop() BAutolock _(this); - bool sendToLastFocus = false; + EventTarget* current = NULL; + EventTarget* previous = NULL; bool pointerEvent = false; bool keyboardEvent = false; bool addedTokens = false; @@ -704,45 +570,37 @@ EventDispatcher::_EventLoop() switch (event->what) { case B_MOUSE_MOVED: { + BPoint where; + if (event->FindPoint("where", &where) == B_OK) + fLastCursorPosition = where; + if (!HasCursorThread()) { // there is no cursor thread, we need to move the cursor ourselves BAutolock _(fCursorLock); - BPoint where; - if (fHWInterface != NULL && event->FindPoint("where", &where) == B_OK) - fHWInterface->MoveCursorTo(where.x, where.y); - } - - if (fTransit) { - // target has changed, we need to add the be:transit field - // to the message - if (fLastFocus != NULL) { - addedTokens = _AddTokens(event, fLastFocus, B_POINTER_EVENTS); - _SendMessage(fLastFocus->Messenger(), event, - kMouseTransitImportance); - sendToLastFocus = true; - - // we no longer need the last focus messenger - _UnsetLastFocus(); + if (fHWInterface != NULL) { + fHWInterface->MoveCursorTo(fLastCursorPosition.x, + fLastCursorPosition.y); } - - fTransit = false; } - BPoint where; - if (event->FindPoint("where", &where) == B_OK) - fLastCursorPosition = where; - // supposed to fall through } case B_MOUSE_DOWN: case B_MOUSE_UP: + { #ifdef TRACE_EVENTS if (event->what != B_MOUSE_MOVED) - printf("mouse up/down event, focus = %p\n", fFocus); + printf("mouse up/down event, previous target = %p\n", fPreviousMouseTarget); #endif - if (fMouseFilter != NULL - && fMouseFilter->Filter(event, NULL) == B_SKIP_MESSAGE) { + pointerEvent = true; + + if (fMouseFilter == NULL) + break; + + EventTarget* mouseTarget = fPreviousMouseTarget; + int32 viewToken = B_NULL_TOKEN; + if (fMouseFilter->Filter(event, &mouseTarget, &viewToken) == B_SKIP_MESSAGE) { // this is a work-around if the wrong B_MOUSE_UP // event is filtered out if (event->what == B_MOUSE_UP) { @@ -763,31 +621,30 @@ EventDispatcher::_EventLoop() event->RemoveName("where"); event->AddPoint("screen_where", fLastCursorPosition); - pointerEvent = true; + if (event->what == B_MOUSE_MOVED + && fPreviousMouseTarget != NULL + && mouseTarget != fPreviousMouseTarget) { + // target has changed, we need to notify the previous target + // that the mouse has exited its views + addedTokens = _AddTokens(event, fPreviousMouseTarget, + B_POINTER_EVENTS); + _SendMessage(fPreviousMouseTarget->Messenger(), event, + kMouseTransitImportance); + previous = fPreviousMouseTarget; + } - if (fFocus != NULL) { - int32 viewToken; + current = fPreviousMouseTarget = mouseTarget; - addedTokens |= _AddTokens(event, fFocus, B_POINTER_EVENTS); - if (addedTokens) - _SetFeedFocus(event); - else if (fFocusGotExitTransit) { - // TODO: this is a temporary hack to not continue to - // send mouse messages to the client when the mouse - // pointer is not over it - if (event->FindInt32("_view_token", &viewToken) != B_OK) - break; + if (current != NULL) { + addedTokens |= _AddTokens(event, current, B_POINTER_EVENTS); + if (viewToken != B_NULL_TOKEN) + event->AddInt32("_view_token", viewToken); - fFocusGotExitTransit = false; - } else if (event->what == B_MOUSE_MOVED) { - if (event->FindInt32("_view_token", &viewToken) != B_OK) - fFocusGotExitTransit = true; - } - - _SendMessage(fFocus->Messenger(), event, event->what == B_MOUSE_MOVED + _SendMessage(current->Messenger(), event, event->what == B_MOUSE_MOVED ? kMouseMovedImportance : kStandardImportance); } break; + } case B_KEY_DOWN: case B_KEY_UP: @@ -797,12 +654,12 @@ EventDispatcher::_EventLoop() ETRACE(("key event, focus = %p\n", fFocus)); if (fKeyboardFilter != NULL - && fKeyboardFilter->Filter(event, NULL) == B_SKIP_MESSAGE) + && fKeyboardFilter->Filter(event, &fFocus) == B_SKIP_MESSAGE) break; keyboardEvent = true; - if (fFocus != NULL && _AddTokens(event, fFocus, B_KEYBOARD_EVENTS)) { + if (current != NULL && _AddTokens(event, fFocus, B_KEYBOARD_EVENTS)) { // if tokens were added, we need to explicetly suspend // focus in the event - if not, the event is simply not // forwarded to the target @@ -815,8 +672,10 @@ EventDispatcher::_EventLoop() // supposed to fall through default: - if (fFocus != NULL && (!fSuspendFocus || addedTokens)) - _SendMessage(fFocus->Messenger(), event, kStandardImportance); + current = fFocus; + + if (current != NULL && (!fSuspendFocus || addedTokens)) + _SendMessage(current->Messenger(), event, kStandardImportance); break; } @@ -833,13 +692,11 @@ EventDispatcher::_EventLoop() event->RemoveName("_view_token"); } - BAutolock _temp(fListenerLock); - for (int32 i = fTargets.CountItems(); i-- > 0;) { - Target* target = fTargets.ItemAt(i); + EventTarget* target = fTargets.ItemAt(i); // we already sent the event to the all focus and last focus tokens - if (fFocus == target || (sendToLastFocus && fLastFocus == target)) + if (current == target || previous == target) continue; // don't send the message if there are no tokens for this event @@ -851,7 +708,6 @@ EventDispatcher::_EventLoop() ? kMouseMovedImportance : kListenerImportance)) { // the target doesn't seem to exist anymore, let's remove it fTargets.RemoveItemAt(i); - delete target; } } diff --git a/src/servers/app/EventDispatcher.h b/src/servers/app/EventDispatcher.h index 5c2a943678..c0d78f97b4 100644 --- a/src/servers/app/EventDispatcher.h +++ b/src/servers/app/EventDispatcher.h @@ -10,14 +10,52 @@ #include +#include #include #include -class BMessageFilter; class EventStream; class HWInterface; +struct event_listener; + + +class EventTarget { + public: + EventTarget(); + ~EventTarget(); + + void SetTo(const BMessenger& messenger); + BMessenger& Messenger() { return fMessenger; } + + event_listener* FindListener(int32 token, int32* _index = NULL); + bool AddListener(int32 token, uint32 eventMask, uint32 options, + bool temporary); + void RemoveListener(event_listener* listener, bool temporary); + + bool RemoveListener(int32 token); + bool RemoveTemporaryListener(int32 token); + void RemoveTemporaryListeners(); + + bool IsEmpty() const { return fListeners.IsEmpty(); } + + int32 CountListeners() const { return fListeners.CountItems(); } + event_listener* ListenerAt(int32 index) const + { return fListeners.ItemAt(index); } + + private: + bool _RemoveTemporaryListener(event_listener* listener, int32 index); + + BObjectList fListeners; + BMessenger fMessenger; +}; + +class EventFilter { + public: + virtual filter_result Filter(BMessage* event, EventTarget** _target, + int32* _viewToken = NULL) = 0; +}; class EventDispatcher : public BLocker { public: @@ -27,17 +65,17 @@ class EventDispatcher : public BLocker { status_t SetTo(EventStream* stream); status_t InitCheck(); - void SetFocus(const BMessenger* messenger); + void RemoveTarget(EventTarget& target); - bool AddListener(const BMessenger& messenger, int32 token, + bool AddListener(EventTarget& target, int32 token, uint32 eventMask, uint32 options); - bool AddTemporaryListener(const BMessenger& messenger, + bool AddTemporaryListener(EventTarget& target, int32 token, uint32 eventMask, uint32 options); - void RemoveListener(const BMessenger& messenger, int32 token); - void RemoveTemporaryListener(const BMessenger& messenger, int32 token); + void RemoveListener(EventTarget& target, int32 token); + void RemoveTemporaryListener(EventTarget& target, int32 token); - void SetMouseFilter(BMessageFilter* filter); - void SetKeyboardFilter(BMessageFilter* filter); + void SetMouseFilter(EventFilter* filter); + void SetKeyboardFilter(EventFilter* filter); void GetMouse(BPoint& where, int32& buttons); @@ -45,26 +83,20 @@ class EventDispatcher : public BLocker { void SetHWInterface(HWInterface* interface); private: - struct event_listener; - class Target; - status_t _Run(); void _Unset(); bool _SendMessage(BMessenger& messenger, BMessage* message, float importance); - bool _AddTokens(BMessage* message, Target* target, uint32 eventMask); + bool _AddTokens(BMessage* message, EventTarget* target, uint32 eventMask); void _RemoveTokens(BMessage* message); void _SetFeedFocus(BMessage* message); void _UnsetFeedFocus(BMessage* message); - void _UnsetLastFocus(); + void _SetMouseTarget(const BMessenger* messenger); + void _UnsetLastMouseTarget(); - Target* _FindTarget(const BMessenger& messenger, int32* _index = NULL); - Target* _AddTarget(const BMessenger& messenger); - void _RemoveTarget(Target* target); - - bool _AddListener(const BMessenger& messenger, int32 token, + bool _AddListener(EventTarget& target, int32 token, uint32 eventMask, uint32 options, bool temporary); void _RemoveTemporaryListeners(); @@ -79,22 +111,18 @@ class EventDispatcher : public BLocker { thread_id fThread; thread_id fCursorThread; - Target* fFocus; - Target* fLastFocus; - bool fTransit; - bool fFocusGotExitTransit; + EventTarget* fPreviousMouseTarget; + EventTarget* fFocus; bool fSuspendFocus; - BMessageFilter* fMouseFilter; - BMessageFilter* fKeyboardFilter; + EventFilter* fMouseFilter; + EventFilter* fKeyboardFilter; - BObjectList fTargets; + BObjectList fTargets; BPoint fLastCursorPosition; int32 fLastButtons; - BLocker fListenerLock; - // temporary locker until we have an actual locking model BLocker fCursorLock; HWInterface* fHWInterface; }; diff --git a/src/servers/app/Layer.cpp b/src/servers/app/Layer.cpp index 6eb86b3fa9..07f6bf3e27 100644 --- a/src/servers/app/Layer.cpp +++ b/src/servers/app/Layer.cpp @@ -834,26 +834,23 @@ Layer::CopyBits(BRect& src, BRect& dst, int32 xOffset, int32 yOffset) void -Layer::MouseDown(BMessage *msg, BPoint where) +Layer::MouseDown(BMessage *msg, BPoint where, int32* _viewToken) { + *_viewToken = ViewToken(); } void -Layer::MouseUp(BMessage *msg, BPoint where) +Layer::MouseUp(BMessage *msg, BPoint where, int32* _viewToken) { + *_viewToken = ViewToken(); } void -Layer::MouseMoved(BMessage *msg, BPoint where) -{ -} - - -void -Layer::MouseWheelChanged(BMessage *msg, BPoint where) +Layer::MouseMoved(BMessage *msg, BPoint where, int32* _viewToken) { + *_viewToken = ViewToken(); } diff --git a/src/servers/app/Layer.h b/src/servers/app/Layer.h index 9c038b5e7c..4368914228 100644 --- a/src/servers/app/Layer.h +++ b/src/servers/app/Layer.h @@ -151,10 +151,9 @@ class Layer { int32 xOffset, int32 yOffset); // input handling - virtual void MouseDown(BMessage *msg, BPoint where); - virtual void MouseUp(BMessage *msg, BPoint where); - virtual void MouseMoved(BMessage *msg, BPoint where); - virtual void MouseWheelChanged(BMessage *msg, BPoint where); + virtual void MouseDown(BMessage *msg, BPoint where, int32* _viewToken); + virtual void MouseUp(BMessage *msg, BPoint where, int32* _viewToken); + virtual void MouseMoved(BMessage *msg, BPoint where, int32* _viewToken); virtual void WorkspaceActivated(int32 index, bool active); virtual void WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces); diff --git a/src/servers/app/RootLayer.cpp b/src/servers/app/RootLayer.cpp index eef42d03c4..69abfc8f55 100644 --- a/src/servers/app/RootLayer.cpp +++ b/src/servers/app/RootLayer.cpp @@ -55,7 +55,7 @@ RootLayer::RootLayer(const char *name, Desktop *desktop, DrawingEngine *driver) : Layer(BRect(0, 0, 0, 0), name, 0, B_FOLLOW_ALL, B_WILL_DRAW, driver), fDesktop(desktop), fDragMessage(NULL), - fMouseEventLayer(NULL), + fMouseEventWindow(NULL), fAllRegionsLock("root layer region lock"), fDirtyForRedraw(), @@ -172,20 +172,15 @@ RootLayer::_SetFocus(WindowLayer* focus, BRegion& update) if (fFocus != NULL) { update.Include(&fFocus->VisibleRegion()); - //fFocus->SetFocus(false); + fFocus->SetFocus(false); } fFocus = focus; if (focus != NULL) { - // TODO: the unlocking is evil, but there is currently no cleaner way to do this, ugh... - // (this is also responsible for an occasional crash on quit) - Unlock(); - fDesktop->EventDispatcher().SetFocus(&focus->Window()->FocusMessenger()); - Lock(); update.Include(&focus->VisibleRegion()); - } else - fDesktop->EventDispatcher().SetFocus(NULL); + focus->SetFocus(true); + } return true; } @@ -194,6 +189,8 @@ RootLayer::_SetFocus(WindowLayer* focus, BRegion& update) bool RootLayer::SetFocus(WindowLayer* focus) { + BAutolock _(fAllRegionsLock); + BRegion update; bool success = _SetFocus(focus, update); if (!success) @@ -524,15 +521,15 @@ RootLayer::_WindowsChanged(BRegion& region) } -Layer* -RootLayer::_ChildAt(BPoint where) +WindowLayer* +RootLayer::WindowAt(BPoint where) { if (VisibleRegion().Contains(where)) return NULL; for (Layer* child = LastChild(); child; child = child->PreviousLayer()) { if (child->FullVisible().Contains(where)) - return child; + return dynamic_cast(child); } return NULL; @@ -543,48 +540,17 @@ RootLayer::_ChildAt(BPoint where) void -RootLayer::MouseEventHandler(BMessage *event) +RootLayer::SetMouseEventWindow(WindowLayer* window) { - BPoint where; - if (event->FindPoint("where", &where) != B_OK) - return; - - Layer* layer = fMouseEventLayer; - if (layer == NULL) { - layer = _ChildAt(where); - if (layer == NULL) - return; - } - - switch (event->what) { - case B_MOUSE_DOWN: - layer->MouseDown(event, where); - break; - - case B_MOUSE_UP: - layer->MouseUp(event, where); - SetMouseEventLayer(NULL); - break; - - case B_MOUSE_MOVED: - layer->MouseMoved(event, where); - break; - } -} - - -void -RootLayer::SetMouseEventLayer(Layer* layer) -{ - fMouseEventLayer = layer; + fMouseEventWindow = window; } void RootLayer::LayerRemoved(Layer* layer) { - if (fMouseEventLayer == layer) - fMouseEventLayer = NULL; + if (fMouseEventWindow == layer) + fMouseEventWindow = NULL; } diff --git a/src/servers/app/RootLayer.h b/src/servers/app/RootLayer.h index b0ccb6ff6c..c0e2a5486a 100644 --- a/src/servers/app/RootLayer.h +++ b/src/servers/app/RootLayer.h @@ -57,7 +57,6 @@ class RootLayer : public Layer { void HideWindowLayer(WindowLayer* windowLayer); void ShowWindowLayer(WindowLayer* windowLayer, bool toFront = true); -// void RevealNewWMState(Workspace::State &oldWMState); bool SetFocus(WindowLayer* focus); WindowLayer* Focus() const { return fFocus; } WindowLayer* Front() const { return fFront; } @@ -67,15 +66,13 @@ class RootLayer : public Layer { void SetWorkspacesLayer(Layer* layer) { fWorkspacesLayer = layer; } Layer* WorkspacesLayer() const { return fWorkspacesLayer; } -#if 0 - void SetBGColor(const RGBColor &col); - RGBColor BGColor(void) const; -#endif - void SetDragMessage(BMessage *msg); BMessage* DragMessage() const; - void SetMouseEventLayer(Layer* layer); + WindowLayer* WindowAt(BPoint where); + + WindowLayer* MouseEventWindow() const { return fMouseEventWindow; } + void SetMouseEventWindow(WindowLayer* layer); void LayerRemoved(Layer* layer); @@ -100,8 +97,6 @@ class RootLayer : public Layer { void AddWindowLayer(WindowLayer* windowLayer); void RemoveWindowLayer(WindowLayer* windowLayer); - void MouseEventHandler(BMessage *msg); - private: bool _SetFocus(WindowLayer* focus, BRegion& update); void _SetFront(WindowLayer* front, BRegion& update); @@ -112,11 +107,9 @@ class RootLayer : public Layer { void _WindowsChanged(BRegion& region); void _UpdateWorkspace(Workspace& workspace); - Layer* _ChildAt(BPoint where); - Desktop* fDesktop; BMessage* fDragMessage; - Layer* fMouseEventLayer; + WindowLayer* fMouseEventWindow; BLocker fAllRegionsLock; diff --git a/src/servers/app/ServerWindow.cpp b/src/servers/app/ServerWindow.cpp index e15649d315..871191ac66 100644 --- a/src/servers/app/ServerWindow.cpp +++ b/src/servers/app/ServerWindow.cpp @@ -159,6 +159,8 @@ ServerWindow::ServerWindow(const char *title, ServerApp *app, BMessenger::Private(fHandlerMessenger).SetTo(fClientTeam, looperPort, clientToken); + fEventTarget.SetTo(fFocusMessenger); + fDeathSemaphore = create_sem(0, "window death"); } @@ -680,7 +682,7 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link) } if (fCurrentLayer->EventMask() != 0) { - fDesktop->EventDispatcher().RemoveListener(FocusMessenger(), + fDesktop->EventDispatcher().RemoveListener(EventTarget(), fCurrentLayer->ViewToken()); } @@ -721,6 +723,8 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link) case AS_LAYER_SET_EVENT_MASK: { STRACE(("ServerWindow %s: Message AS_LAYER_SET_MOUSE_EVENT_MASK: Layer name: %s\n", fTitle, fCurrentLayer->Name())); + rootLayer->Unlock(); + // we don't want RootLayer to be locked while we play with the events uint32 eventMask, options; @@ -729,33 +733,38 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link) fCurrentLayer->SetEventMask(eventMask, options); if (eventMask != 0 || options != 0) { - fDesktop->EventDispatcher().AddListener(FocusMessenger(), + fDesktop->EventDispatcher().AddListener(EventTarget(), fCurrentLayer->ViewToken(), eventMask, options); } else { - fDesktop->EventDispatcher().RemoveListener(FocusMessenger(), + fDesktop->EventDispatcher().RemoveListener(EventTarget(), fCurrentLayer->ViewToken()); } } + + rootLayer->Lock(); break; } case AS_LAYER_SET_MOUSE_EVENT_MASK: { STRACE(("ServerWindow %s: Message AS_LAYER_SET_MOUSE_EVENT_MASK: Layer name: %s\n", fTitle, fCurrentLayer->Name())); + rootLayer->Unlock(); + // we don't want RootLayer to be locked while we play with the events uint32 eventMask, options; link.Read(&eventMask); if (link.Read(&options) == B_OK) { if (eventMask != 0 || options != 0) { - fDesktop->EventDispatcher().AddTemporaryListener(FocusMessenger(), + fDesktop->EventDispatcher().AddTemporaryListener(EventTarget(), fCurrentLayer->ViewToken(), eventMask, options); } else { - fDesktop->EventDispatcher().RemoveTemporaryListener(FocusMessenger(), + fDesktop->EventDispatcher().RemoveTemporaryListener(EventTarget(), fCurrentLayer->ViewToken()); } } // TODO: support B_LOCK_WINDOW_FOCUS option in RootLayer + rootLayer->Lock(); break; } case AS_LAYER_MOVE_TO: diff --git a/src/servers/app/ServerWindow.h b/src/servers/app/ServerWindow.h index b8793f4824..ad2f78c21d 100644 --- a/src/servers/app/ServerWindow.h +++ b/src/servers/app/ServerWindow.h @@ -13,6 +13,7 @@ #define SERVER_WINDOW_H +#include "EventDispatcher.h" #include "MessageLooper.h" #include @@ -59,6 +60,8 @@ public: virtual bool Run(); virtual port_id MessagePort() const { return fMessagePort; } + ::EventTarget& EventTarget() { return fEventTarget; } + void ReplaceDecorator(); void Show(); void Hide(); @@ -135,6 +138,7 @@ private: port_id fClientLooperPort; BMessenger fFocusMessenger; BMessenger fHandlerMessenger; + ::EventTarget fEventTarget; BMessage fClientViewsWithInvalidCoords; diff --git a/src/servers/app/WindowLayer.cpp b/src/servers/app/WindowLayer.cpp index 908a44dc6d..231ce98091 100644 --- a/src/servers/app/WindowLayer.cpp +++ b/src/servers/app/WindowLayer.cpp @@ -70,6 +70,8 @@ WindowLayer::WindowLayer(const BRect &frame, fMouseButtons(0), fLastMousePosition(-1.0, -1.0), + fIsFocus(false), + fIsClosing(false), fIsMinimizing(false), fIsZooming(false), @@ -148,7 +150,7 @@ WindowLayer::Draw(const BRect& updateRect) // if we have a visible region, it is decorator's one. if (fDecorator) { - fDecorator->SetFocus(GetRootLayer()->Focus() == this); + fDecorator->SetFocus(IsFocus()); fDecorator->Draw(updateRect); } } @@ -389,7 +391,7 @@ WindowLayer::GetSizeLimits(float* minWidth, float* maxWidth, void -WindowLayer::MouseDown(BMessage *msg, BPoint where) +WindowLayer::MouseDown(BMessage *msg, BPoint where, int32* _viewToken) { Desktop* desktop = Window()->App()->GetDesktop(); @@ -403,8 +405,8 @@ WindowLayer::MouseDown(BMessage *msg, BPoint where) if (fDecorator) action = _ActionFor(msg); - // deactivate border buttons on first click(select) - if (GetRootLayer()->Focus() != this && action != DEC_MOVETOBACK + // deactivate border buttons on first click (select) + if (!IsFocus() && action != DEC_MOVETOBACK && action != DEC_RESIZE && action != DEC_SLIDETAB) action = DEC_DRAG; @@ -454,12 +456,12 @@ WindowLayer::MouseDown(BMessage *msg, BPoint where) if (action == DEC_MOVETOBACK) { desktop->SendBehindWindow(this, NULL); } else { - GetRootLayer()->SetMouseEventLayer(this); + GetRootLayer()->SetMouseEventWindow(this); desktop->ActivateWindow(this); } } else if (target != NULL) { // clicking a simple Layer. - if (GetRootLayer()->Focus() != this) { + if (!IsFocus()) { DesktopSettings desktopSettings(desktop); // not in FFM mode? @@ -470,14 +472,13 @@ WindowLayer::MouseDown(BMessage *msg, BPoint where) return; } - msg->AddInt32("_view_token", target->ViewToken()); - target->MouseDown(msg, where); + target->MouseDown(msg, where, _viewToken); } } void -WindowLayer::MouseUp(BMessage *msg, BPoint where) +WindowLayer::MouseUp(BMessage *msg, BPoint where, int32* _viewToken) { bool invalidate = false; if (fDecorator) { @@ -517,15 +518,13 @@ WindowLayer::MouseUp(BMessage *msg, BPoint where) fIsSlidingTab = false; Layer* target = LayerAt(where); - if (target != NULL && target != this) { - msg->AddInt32("_view_token", target->ViewToken()); - target->MouseUp(msg, where); - } + if (target != NULL && target != this) + target->MouseUp(msg, where, _viewToken); } void -WindowLayer::MouseMoved(BMessage *msg, BPoint where) +WindowLayer::MouseMoved(BMessage *msg, BPoint where, int32* _viewToken) { if (fDecorator) { // TODO: present behavior is not fine! @@ -560,14 +559,12 @@ WindowLayer::MouseMoved(BMessage *msg, BPoint where) Desktop* desktop = Window()->App()->GetDesktop(); DesktopSettings desktopSettings(desktop); - if (desktopSettings.MouseMode() != B_NORMAL_MOUSE && GetRootLayer()->Focus() != this) + if (desktopSettings.MouseMode() != B_NORMAL_MOUSE && !IsFocus()) GetRootLayer()->SetFocus(this); Layer* target = LayerAt(where); - if (target != NULL && target != this) { - msg->AddInt32("_view_token", target->ViewToken()); - target->MouseMoved(msg, where); - } + if (target != NULL && target != this) + target->MouseMoved(msg, where, _viewToken); } diff --git a/src/servers/app/WindowLayer.h b/src/servers/app/WindowLayer.h index c6e565ee32..a90db76ed4 100644 --- a/src/servers/app/WindowLayer.h +++ b/src/servers/app/WindowLayer.h @@ -84,9 +84,9 @@ class WindowLayer : public Layer { float* minHeight, float* maxHeight) const; - virtual void MouseDown(BMessage *msg, BPoint where); - virtual void MouseUp(BMessage *msg, BPoint where); - virtual void MouseMoved(BMessage *msg, BPoint where); + virtual void MouseDown(BMessage *msg, BPoint where, int32* _viewToken); + virtual void MouseUp(BMessage *msg, BPoint where, int32* _viewToken); + virtual void MouseMoved(BMessage *msg, BPoint where, int32* _viewToken); // click_type ActionFor(const BMessage *msg) // { return _ActionFor(evt); } @@ -100,6 +100,9 @@ class WindowLayer : public Layer { void UpdateFont(); void UpdateScreen(); + bool IsFocus() const { return fIsFocus; } + void SetFocus(bool focus) { fIsFocus = focus; } + inline Decorator* GetDecorator() const { return fDecorator; } inline int32 Look() const { return fLook; } @@ -151,6 +154,8 @@ class WindowLayer : public Layer { BPoint fLastMousePosition; BPoint fResizingClickOffset; + bool fIsFocus; + bool fIsClosing; bool fIsMinimizing; bool fIsZooming;