From 955307393fad754aaff5d372cb1cc139e6c4159c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Fri, 17 Oct 2008 21:37:10 +0000 Subject: [PATCH] * Reworked EventDispatcher::SendFakeMouseMoved() after an idea by Stephan that solves most app_server locking headaches: it now works asynchronously, and therefore doesn't need to lock the EventDispatcher anymore. * EventStreams now allow to inject messages into the stream to allow the above functionality. * InputServerStream::GetNextEvent() no longer returns when there is no event. * Desktop::ActivateWindow() now locks all windows before checking the workspaces of the windows, fixing a race condition that could lead to Window::Foremost() being called for a window that isn't on the current workspace, leading to a crash. * I currently cannot access Trac, but I recall there should be an open bug report about this. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@28224 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/servers/app/Desktop.cpp | 32 ++++++++++------ src/servers/app/Desktop.h | 1 + src/servers/app/EventDispatcher.cpp | 58 ++++++++++++++++++++++++----- src/servers/app/EventDispatcher.h | 1 + src/servers/app/EventStream.cpp | 21 ++++++++++- src/servers/app/EventStream.h | 4 ++ 6 files changed, 94 insertions(+), 23 deletions(-) diff --git a/src/servers/app/Desktop.cpp b/src/servers/app/Desktop.cpp index 794cf7cb1a..68d8a84ac1 100644 --- a/src/servers/app/Desktop.cpp +++ b/src/servers/app/Desktop.cpp @@ -1326,8 +1326,6 @@ Desktop::RemoveWorkspacesView(WorkspacesView* view) This has only to be done in case the view changed without user interaction, ie. because of a workspace change or a closing window. - - Windows must not be locked when calling this method. */ void Desktop::_SendFakeMouseMoved(Window* window) @@ -1520,6 +1518,9 @@ Desktop::ActivateWindow(Window* window) // TODO: take care about floating windows + if (!LockAllWindows()) + return; + bool windowOnOtherWorkspace = !window->InWorkspace(fCurrentWorkspace); if (windowOnOtherWorkspace) { if ((window->Flags() & B_NO_WORKSPACE_ACTIVATION) == 0 @@ -1535,13 +1536,12 @@ Desktop::ActivateWindow(Window* window) break; } } - } else if ((window->Flags() & B_NOT_ANCHORED_ON_ACTIVATE) == 0) + } else if ((window->Flags() & B_NOT_ANCHORED_ON_ACTIVATE) == 0) { + UnlockAllWindows(); return; + } } - if (!LockAllWindows()) - return; - if (windowOnOtherWorkspace && (window->Flags() & B_NOT_ANCHORED_ON_ACTIVATE) != 0) { // bring the window to the current workspace @@ -1554,12 +1554,7 @@ Desktop::ActivateWindow(Window* window) // Unlike WindowAction(), this is called from the application itself, // so we will just unminimize the window here. window->SetMinimized(false); - UnlockAllWindows(); - ShowWindow(window); - - if (!LockAllWindows()) - return; } if (window == FrontWindow()) { @@ -2368,7 +2363,7 @@ Desktop::ViewUnderMouse(const Window* window) } -Window * +Window* Desktop::FindWindowByClientToken(int32 token, team_id teamID) { for (Window *window = fAllWindows.FirstWindow(); window != NULL; @@ -2383,6 +2378,19 @@ Desktop::FindWindowByClientToken(int32 token, team_id teamID) } +::EventTarget* +Desktop::FindTarget(BMessenger& messenger) +{ + for (Window *window = fAllWindows.FirstWindow(); window != NULL; + window = window->NextWindow(kAllWindowList)) { + if (window->EventTarget().Messenger() == messenger) + return &window->EventTarget(); + } + + return NULL; +} + + void Desktop::MinimizeApplication(team_id team) { diff --git a/src/servers/app/Desktop.h b/src/servers/app/Desktop.h index 0f7660975e..7009962617 100644 --- a/src/servers/app/Desktop.h +++ b/src/servers/app/Desktop.h @@ -167,6 +167,7 @@ class Desktop : public MessageLooper, public ScreenOwner { Window* FindWindowByClientToken(int32 token, team_id teamID); + EventTarget* FindTarget(BMessenger& messenger); #if USE_MULTI_LOCKER bool LockSingleWindow() diff --git a/src/servers/app/EventDispatcher.cpp b/src/servers/app/EventDispatcher.cpp index 1a221cd1f1..6706f12144 100644 --- a/src/servers/app/EventDispatcher.cpp +++ b/src/servers/app/EventDispatcher.cpp @@ -17,6 +17,7 @@ #include "ServerBitmap.h" #include +#include #include #include @@ -72,6 +73,8 @@ struct event_listener { static const char* kTokenName = "_token"; +static const uint32 kFakeMouseMoved = 'fake'; + static const float kMouseMovedImportance = 0.1f; static const float kMouseTransitImportance = 1.0f; static const float kStandardImportance = 0.9f; @@ -494,7 +497,44 @@ EventDispatcher::GetMouse(BPoint& where, int32& buttons) void EventDispatcher::SendFakeMouseMoved(EventTarget& target, int32 viewToken) { - BAutolock _(this); + if (fStream == NULL) + return; + + BMessage* fakeMove = new BMessage(kFakeMouseMoved); + if (fakeMove == NULL) + return; + + fakeMove->AddMessenger("target", target.Messenger()); + fakeMove->AddInt32("view_token", viewToken); + + fStream->InsertEvent(fakeMove); +} + + +void +EventDispatcher::_SendFakeMouseMoved(BMessage* message) +{ + BMessenger target; + int32 viewToken; + if (message->FindInt32("view_token", &viewToken) != B_OK + || message->FindMessenger("target", &target) != B_OK) + return; + + if (fDesktop == NULL) + return; + + // Check if the target is still valid + ::EventTarget* eventTarget = NULL; + + fDesktop->LockSingleWindow(); + + if (target.IsValid()) + eventTarget = fDesktop->FindTarget(target); + + fDesktop->UnlockSingleWindow(); + + if (eventTarget == NULL) + return; BMessage moved(B_MOUSE_MOVED); moved.AddPoint("screen_where", fLastCursorPosition); @@ -504,7 +544,7 @@ EventDispatcher::SendFakeMouseMoved(EventTarget& target, int32 viewToken) moved.AddMessage("be:drag_message", &fDragMessage); if (fPreviousMouseTarget != NULL - && fPreviousMouseTarget != &target) { + && fPreviousMouseTarget->Messenger() != target) { _AddTokens(&moved, fPreviousMouseTarget, B_POINTER_EVENTS); _SendMessage(fPreviousMouseTarget->Messenger(), &moved, kMouseTransitImportance); @@ -515,8 +555,9 @@ EventDispatcher::SendFakeMouseMoved(EventTarget& target, int32 viewToken) moved.AddInt32("_view_token", viewToken); // this only belongs to the new target - _SendMessage(target.Messenger(), &moved, kMouseTransitImportance); - fPreviousMouseTarget = ⌖ + _SendMessage(target, &moved, kMouseTransitImportance); + + fPreviousMouseTarget = eventTarget; } @@ -718,12 +759,6 @@ EventDispatcher::_EventLoop() { BMessage* event; while (fStream->GetNextEvent(&event)) { - if (event == NULL) { - // may happen in out of memory situations or junk at the port - // we can't do anything about those yet - continue; - } - BAutolock _(this); fLastUpdate = system_time(); @@ -734,6 +769,9 @@ EventDispatcher::_EventLoop() bool addedTokens = false; switch (event->what) { + case kFakeMouseMoved: + _SendFakeMouseMoved(event); + break; case B_MOUSE_MOVED: { BPoint where; diff --git a/src/servers/app/EventDispatcher.h b/src/servers/app/EventDispatcher.h index ab079e84f5..656234ddbb 100644 --- a/src/servers/app/EventDispatcher.h +++ b/src/servers/app/EventDispatcher.h @@ -102,6 +102,7 @@ class EventDispatcher : public BLocker { status_t _Run(); void _Unset(); + void _SendFakeMouseMoved(BMessage* message); bool _SendMessage(BMessenger& messenger, BMessage* message, float importance); diff --git a/src/servers/app/EventStream.cpp b/src/servers/app/EventStream.cpp index 4af3f6cb1c..73f968680f 100644 --- a/src/servers/app/EventStream.cpp +++ b/src/servers/app/EventStream.cpp @@ -120,7 +120,7 @@ InputServerStream::UpdateScreenBounds(BRect bounds) bool InputServerStream::GetNextEvent(BMessage** _event) { - if (fEvents.IsEmpty()) { + while (fEvents.IsEmpty()) { // wait for new events BMessage* event; status_t status = _MessageFromPort(&event); @@ -191,6 +191,21 @@ InputServerStream::GetNextCursorPosition(BPoint &where) } +status_t +InputServerStream::InsertEvent(BMessage* event) +{ + fEvents.AddMessage(event); + status_t status = write_port_etc(fPort, 'insm', NULL, 0, B_RELATIVE_TIMEOUT, + 0); + if (status == B_BAD_PORT_ID) + return status; + + // If the port is full, we obviously don't care to report this, as we + // already placed our message. + return B_OK; +} + + BMessage* InputServerStream::PeekLatestMouseMoved() { @@ -231,6 +246,10 @@ InputServerStream::_MessageFromPort(BMessage** _message, bigtime_t timeout) // this will cause GetNextEvent() to return false return B_BAD_PORT_ID; } + if (code == 'insm') { + // a message has been inserted into our queue + return B_INTERRUPTED; + } // we have the message, now let's unflatten it diff --git a/src/servers/app/EventStream.h b/src/servers/app/EventStream.h index 4cc78fb792..6063b2ce29 100644 --- a/src/servers/app/EventStream.h +++ b/src/servers/app/EventStream.h @@ -32,6 +32,8 @@ class EventStream { virtual bool GetNextEvent(BMessage** _event) = 0; virtual bool GetNextCursorPosition(BPoint& where); + virtual status_t InsertEvent(BMessage* event) = 0; + virtual BMessage* PeekLatestMouseMoved() = 0; }; @@ -55,6 +57,8 @@ class InputServerStream : public EventStream { virtual bool GetNextEvent(BMessage** _event); virtual bool GetNextCursorPosition(BPoint& where); + virtual status_t InsertEvent(BMessage* event); + virtual BMessage* PeekLatestMouseMoved(); private: