* 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
This commit is contained in:
Axel Dörfler
2008-10-17 21:37:10 +00:00
parent 7ab39de989
commit 955307393f
6 changed files with 94 additions and 23 deletions
+20 -12
View File
@@ -1326,8 +1326,6 @@ Desktop::RemoveWorkspacesView(WorkspacesView* view)
This has only to be done in case the view changed without user interaction, This has only to be done in case the view changed without user interaction,
ie. because of a workspace change or a closing window. ie. because of a workspace change or a closing window.
Windows must not be locked when calling this method.
*/ */
void void
Desktop::_SendFakeMouseMoved(Window* window) Desktop::_SendFakeMouseMoved(Window* window)
@@ -1520,6 +1518,9 @@ Desktop::ActivateWindow(Window* window)
// TODO: take care about floating windows // TODO: take care about floating windows
if (!LockAllWindows())
return;
bool windowOnOtherWorkspace = !window->InWorkspace(fCurrentWorkspace); bool windowOnOtherWorkspace = !window->InWorkspace(fCurrentWorkspace);
if (windowOnOtherWorkspace) { if (windowOnOtherWorkspace) {
if ((window->Flags() & B_NO_WORKSPACE_ACTIVATION) == 0 if ((window->Flags() & B_NO_WORKSPACE_ACTIVATION) == 0
@@ -1535,13 +1536,12 @@ Desktop::ActivateWindow(Window* window)
break; break;
} }
} }
} else if ((window->Flags() & B_NOT_ANCHORED_ON_ACTIVATE) == 0) } else if ((window->Flags() & B_NOT_ANCHORED_ON_ACTIVATE) == 0) {
UnlockAllWindows();
return; return;
}
} }
if (!LockAllWindows())
return;
if (windowOnOtherWorkspace if (windowOnOtherWorkspace
&& (window->Flags() & B_NOT_ANCHORED_ON_ACTIVATE) != 0) { && (window->Flags() & B_NOT_ANCHORED_ON_ACTIVATE) != 0) {
// bring the window to the current workspace // bring the window to the current workspace
@@ -1554,12 +1554,7 @@ Desktop::ActivateWindow(Window* window)
// Unlike WindowAction(), this is called from the application itself, // Unlike WindowAction(), this is called from the application itself,
// so we will just unminimize the window here. // so we will just unminimize the window here.
window->SetMinimized(false); window->SetMinimized(false);
UnlockAllWindows();
ShowWindow(window); ShowWindow(window);
if (!LockAllWindows())
return;
} }
if (window == FrontWindow()) { if (window == FrontWindow()) {
@@ -2368,7 +2363,7 @@ Desktop::ViewUnderMouse(const Window* window)
} }
Window * Window*
Desktop::FindWindowByClientToken(int32 token, team_id teamID) Desktop::FindWindowByClientToken(int32 token, team_id teamID)
{ {
for (Window *window = fAllWindows.FirstWindow(); window != NULL; 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 void
Desktop::MinimizeApplication(team_id team) Desktop::MinimizeApplication(team_id team)
{ {
+1
View File
@@ -167,6 +167,7 @@ class Desktop : public MessageLooper, public ScreenOwner {
Window* FindWindowByClientToken(int32 token, Window* FindWindowByClientToken(int32 token,
team_id teamID); team_id teamID);
EventTarget* FindTarget(BMessenger& messenger);
#if USE_MULTI_LOCKER #if USE_MULTI_LOCKER
bool LockSingleWindow() bool LockSingleWindow()
+48 -10
View File
@@ -17,6 +17,7 @@
#include "ServerBitmap.h" #include "ServerBitmap.h"
#include <MessagePrivate.h> #include <MessagePrivate.h>
#include <MessengerPrivate.h>
#include <ServerProtocol.h> #include <ServerProtocol.h>
#include <TokenSpace.h> #include <TokenSpace.h>
@@ -72,6 +73,8 @@ struct event_listener {
static const char* kTokenName = "_token"; static const char* kTokenName = "_token";
static const uint32 kFakeMouseMoved = 'fake';
static const float kMouseMovedImportance = 0.1f; static const float kMouseMovedImportance = 0.1f;
static const float kMouseTransitImportance = 1.0f; static const float kMouseTransitImportance = 1.0f;
static const float kStandardImportance = 0.9f; static const float kStandardImportance = 0.9f;
@@ -494,7 +497,44 @@ EventDispatcher::GetMouse(BPoint& where, int32& buttons)
void void
EventDispatcher::SendFakeMouseMoved(EventTarget& target, int32 viewToken) 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); BMessage moved(B_MOUSE_MOVED);
moved.AddPoint("screen_where", fLastCursorPosition); moved.AddPoint("screen_where", fLastCursorPosition);
@@ -504,7 +544,7 @@ EventDispatcher::SendFakeMouseMoved(EventTarget& target, int32 viewToken)
moved.AddMessage("be:drag_message", &fDragMessage); moved.AddMessage("be:drag_message", &fDragMessage);
if (fPreviousMouseTarget != NULL if (fPreviousMouseTarget != NULL
&& fPreviousMouseTarget != &target) { && fPreviousMouseTarget->Messenger() != target) {
_AddTokens(&moved, fPreviousMouseTarget, B_POINTER_EVENTS); _AddTokens(&moved, fPreviousMouseTarget, B_POINTER_EVENTS);
_SendMessage(fPreviousMouseTarget->Messenger(), &moved, _SendMessage(fPreviousMouseTarget->Messenger(), &moved,
kMouseTransitImportance); kMouseTransitImportance);
@@ -515,8 +555,9 @@ EventDispatcher::SendFakeMouseMoved(EventTarget& target, int32 viewToken)
moved.AddInt32("_view_token", viewToken); moved.AddInt32("_view_token", viewToken);
// this only belongs to the new target // this only belongs to the new target
_SendMessage(target.Messenger(), &moved, kMouseTransitImportance); _SendMessage(target, &moved, kMouseTransitImportance);
fPreviousMouseTarget = &target;
fPreviousMouseTarget = eventTarget;
} }
@@ -718,12 +759,6 @@ EventDispatcher::_EventLoop()
{ {
BMessage* event; BMessage* event;
while (fStream->GetNextEvent(&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); BAutolock _(this);
fLastUpdate = system_time(); fLastUpdate = system_time();
@@ -734,6 +769,9 @@ EventDispatcher::_EventLoop()
bool addedTokens = false; bool addedTokens = false;
switch (event->what) { switch (event->what) {
case kFakeMouseMoved:
_SendFakeMouseMoved(event);
break;
case B_MOUSE_MOVED: case B_MOUSE_MOVED:
{ {
BPoint where; BPoint where;
+1
View File
@@ -102,6 +102,7 @@ class EventDispatcher : public BLocker {
status_t _Run(); status_t _Run();
void _Unset(); void _Unset();
void _SendFakeMouseMoved(BMessage* message);
bool _SendMessage(BMessenger& messenger, BMessage* message, bool _SendMessage(BMessenger& messenger, BMessage* message,
float importance); float importance);
+20 -1
View File
@@ -120,7 +120,7 @@ InputServerStream::UpdateScreenBounds(BRect bounds)
bool bool
InputServerStream::GetNextEvent(BMessage** _event) InputServerStream::GetNextEvent(BMessage** _event)
{ {
if (fEvents.IsEmpty()) { while (fEvents.IsEmpty()) {
// wait for new events // wait for new events
BMessage* event; BMessage* event;
status_t status = _MessageFromPort(&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* BMessage*
InputServerStream::PeekLatestMouseMoved() InputServerStream::PeekLatestMouseMoved()
{ {
@@ -231,6 +246,10 @@ InputServerStream::_MessageFromPort(BMessage** _message, bigtime_t timeout)
// this will cause GetNextEvent() to return false // this will cause GetNextEvent() to return false
return B_BAD_PORT_ID; 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 // we have the message, now let's unflatten it
+4
View File
@@ -32,6 +32,8 @@ class EventStream {
virtual bool GetNextEvent(BMessage** _event) = 0; virtual bool GetNextEvent(BMessage** _event) = 0;
virtual bool GetNextCursorPosition(BPoint& where); virtual bool GetNextCursorPosition(BPoint& where);
virtual status_t InsertEvent(BMessage* event) = 0;
virtual BMessage* PeekLatestMouseMoved() = 0; virtual BMessage* PeekLatestMouseMoved() = 0;
}; };
@@ -55,6 +57,8 @@ class InputServerStream : public EventStream {
virtual bool GetNextEvent(BMessage** _event); virtual bool GetNextEvent(BMessage** _event);
virtual bool GetNextCursorPosition(BPoint& where); virtual bool GetNextCursorPosition(BPoint& where);
virtual status_t InsertEvent(BMessage* event);
virtual BMessage* PeekLatestMouseMoved(); virtual BMessage* PeekLatestMouseMoved();
private: private: