* 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

View File

@ -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,12 +1536,11 @@ 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) {
@ -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)
{

View File

@ -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()

View File

@ -17,6 +17,7 @@
#include "ServerBitmap.h"
#include <MessagePrivate.h>
#include <MessengerPrivate.h>
#include <ServerProtocol.h>
#include <TokenSpace.h>
@ -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 = &target;
_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;

View File

@ -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);

View File

@ -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

View File

@ -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: