diff --git a/src/servers/app/Desktop.h b/src/servers/app/Desktop.h index 7628c84bb1..87362821e2 100644 --- a/src/servers/app/Desktop.h +++ b/src/servers/app/Desktop.h @@ -10,7 +10,9 @@ #ifndef _DESKTOP_H_ #define _DESKTOP_H_ + #include "CursorManager.h" +#include "EventDispatcher.h" #include "ScreenManager.h" #include "ServerScreen.h" #include "VirtualScreen.h" @@ -104,6 +106,7 @@ class Desktop : public MessageLooper, public ScreenOwner { ::VirtualScreen fVirtualScreen; DesktopSettings::Private* fSettings; port_id fMessagePort; + EventDispatcher fEventDispatcher; port_id fInputPort; BLocker fAppListLock; diff --git a/src/servers/app/EventDispatcher.cpp b/src/servers/app/EventDispatcher.cpp new file mode 100644 index 0000000000..23c09c5137 --- /dev/null +++ b/src/servers/app/EventDispatcher.cpp @@ -0,0 +1,294 @@ +/* + * Copyright 2005, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler, axeld@pinc-software.de + */ + + +#include "EventDispatcher.h" +#include "EventStream.h" +#include "HWInterface.h" + +#include +#include +#include +#include + +#include + + +static const float kMouseMovedImportance = 0.1f; +static const float kMouseTransitImportance = 1.0f; +static const float kStandardImportance = 0.9f; + + +EventDispatcher::EventDispatcher() + : BLocker("event dispatcher"), + fStream(NULL), + fThread(-1), + fCursorThread(-1), + fFocus(NULL), + fLastFocus(NULL), + fTransit(false), + fMouseFilter(NULL), + fKeyFilter(NULL), + fCursorLock("cursor loop lock"), + fHWInterface(NULL) +{ +} + + +EventDispatcher::~EventDispatcher() +{ + _Unset(); +} + + +status_t +EventDispatcher::SetTo(EventStream& stream) +{ + _Unset(); + + fStream = &stream; + return _Run(); +} + + +status_t +EventDispatcher::InitCheck() +{ + if (fStream != NULL) { + if (fThread < B_OK) + return fThread; + + return B_OK; + } + return B_NO_INIT; +} + + +void +EventDispatcher::_Unset() +{ + fStream->SendQuit(); + + wait_for_thread(fThread, NULL); + wait_for_thread(fCursorThread, NULL); + + fThread = fCursorThread = -1; +} + + +status_t +EventDispatcher::_Run() +{ + fThread = spawn_thread(_event_looper, "event loop", + B_REAL_TIME_DISPLAY_PRIORITY - 10, this); + if (fThread < B_OK) + return fThread; + + if (fStream->SupportsCursorThread()) { + fCursorThread = spawn_thread(_cursor_looper, "cursor loop", + B_REAL_TIME_DISPLAY_PRIORITY - 5, this); + if (resume_thread(fCursorThread) != B_OK) { + kill_thread(fCursorThread); + fCursorThread = -1; + } + } + + return resume_thread(fThread); +} + + +void +EventDispatcher::SetFocus(BMessenger* messenger) +{ + BAutolock _(this); + + if (fFocus == messenger) + return; + + delete fLastFocus; + fLastFocus = fFocus; + fFocus = messenger; + fTransit = true; +} + + +void +EventDispatcher::SetMouseFilter(BMessageFilter* filter) +{ + BAutolock _(this); + + if (fMouseFilter == filter) + return; + + delete fMouseFilter; + fMouseFilter = filter; +} + + +void +EventDispatcher::SetKeyFilter(BMessageFilter* filter) +{ + BAutolock _(this); + + if (fKeyFilter == filter) + return; + + delete fKeyFilter; + fKeyFilter = filter; +} + + +bool +EventDispatcher::HasCursorThread() +{ + return fCursorThread >= B_OK; +} + + +/*! + \brief Sets the HWInterface to use when moving the mouse cursor. + \a interface is allowed to be NULL. +*/ +void +EventDispatcher::SetHWInterface(HWInterface* interface) +{ + BAutolock _(fCursorLock); + + fHWInterface = interface; +} + + +void +EventDispatcher::_SendMessage(BMessenger* messenger, BMessage* message, + float importance) +{ + if (messenger == NULL) + return; + + // TODO: add failed messages to a queue, and start dropping them by importance + + status_t status = fFocus->SendMessage(message, (BHandler*)NULL, 100000); + if (status != B_OK) + printf("failed to send message to target: %lx\n", message->what); +} + + +void +EventDispatcher::_SetTransit(BMessage* message, int32 transit) +{ + if (message->ReplaceInt32("be:transit", transit) != B_OK) + message->AddInt32("be:transit", transit); +} + + +void +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); + + switch (event->what) { + case B_MOUSE_MOVED: + 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) { + _SetTransit(event, B_EXITED_VIEW); + _SendMessage(fLastFocus, event, kMouseTransitImportance); + + // we no longer need the last focus messenger + delete fLastFocus; + fLastFocus = NULL; + } + if (fFocus != NULL) { + _SetTransit(event, B_ENTERED_VIEW); + _SendMessage(fLastFocus, event, kMouseTransitImportance); + } + break; + } + // supposed to fall through + case B_MOUSE_DOWN: + case B_MOUSE_UP: + if (fMouseFilter != NULL + && fMouseFilter->Filter(event, NULL) == B_SKIP_MESSAGE) + break; + + _SendMessage(fFocus, event, event->what == B_MOUSE_MOVED + ? kMouseMovedImportance : kStandardImportance); + break; + + case B_KEY_DOWN: + case B_KEY_UP: + case B_UNMAPPED_KEY_DOWN: + case B_UNMAPPED_KEY_UP: + case B_MODIFIERS_CHANGED: + if (fKeyFilter != NULL + && fKeyFilter->Filter(event, NULL) == B_SKIP_MESSAGE) + break; + + default: + _SendMessage(fLastFocus, event, kStandardImportance); + break; + } + + delete event; + } +} + + +void +EventDispatcher::_CursorLoop() +{ + BPoint where; + while (fStream->GetNextCursorPosition(where)) { + BAutolock _(fCursorLock); + + if (fHWInterface != NULL) + fHWInterface->MoveCursorTo(where.x, where.y); + } + + fCursorThread = -1; +} + + +/*static*/ +status_t +EventDispatcher::_event_looper(void* _dispatcher) +{ + EventDispatcher* dispatcher = (EventDispatcher*)_dispatcher; + + dispatcher->_EventLoop(); + return B_OK; +} + + +/*static*/ +status_t +EventDispatcher::_cursor_looper(void* _dispatcher) +{ + EventDispatcher* dispatcher = (EventDispatcher*)_dispatcher; + + dispatcher->_CursorLoop(); + return B_OK; +} diff --git a/src/servers/app/EventDispatcher.h b/src/servers/app/EventDispatcher.h new file mode 100644 index 0000000000..ae1ef933e5 --- /dev/null +++ b/src/servers/app/EventDispatcher.h @@ -0,0 +1,67 @@ +/* + * Copyright 2005, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler, axeld@pinc-software.de + */ +#ifndef EVENT_DISPATCHER_H +#define EVENT_DISPATCHER_H + + +#include + +class BMessageFilter; +class BMessenger; + +class EventStream; +class HWInterface; + + +class EventDispatcher : public BLocker { + public: + EventDispatcher(); + ~EventDispatcher(); + + status_t SetTo(EventStream& stream); + status_t InitCheck(); + + void SetFocus(BMessenger* messenger); + + void SetMouseFilter(BMessageFilter* filter); + void SetKeyFilter(BMessageFilter* filter); + + bool HasCursorThread(); + void SetHWInterface(HWInterface* interface); + + EventStream* Stream(); + + private: + status_t _Run(); + void _Unset(); + + void _SendMessage(BMessenger* messenger, BMessage* message, float importance); + void _SetTransit(BMessage* message, int32 transit); + + void _EventLoop(); + void _CursorLoop(); + + static status_t _event_looper(void* dispatcher); + static status_t _cursor_looper(void* dispatcher); + + private: + EventStream* fStream; + thread_id fThread; + thread_id fCursorThread; + + BMessenger* fFocus; + BMessenger* fLastFocus; + bool fTransit; + BMessageFilter* fMouseFilter; + BMessageFilter* fKeyFilter; + + BLocker fCursorLock; + HWInterface* fHWInterface; +}; + +#endif /* EVENT_DISPATCHER_H */ diff --git a/src/servers/app/EventStream.cpp b/src/servers/app/EventStream.cpp new file mode 100644 index 0000000000..e5ab9cc69a --- /dev/null +++ b/src/servers/app/EventStream.cpp @@ -0,0 +1,197 @@ +/* + * Copyright 2005, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler, axeld@pinc-software.de + */ + + +#include "EventStream.h" + +#include + +#include +#include + + +EventStream::EventStream() +{ +} + + +EventStream::~EventStream() +{ +} + + +bool +EventStream::SupportsCursorThread() const +{ + return false; +} + + +// #pragma mark - + + +InputServerStream::InputServerStream(port_id port, port_id inputServerPort) + : + fPort(port), + fQuitting(false) +{ +} + + +InputServerStream::~InputServerStream() +{ +} + + +bool +InputServerStream::IsValid() +{ + port_info portInfo; + if (fPort < B_OK || get_port_info(fPort, &portInfo) != B_OK) + return false; + + return true; +} + + +void +InputServerStream::SendQuit() +{ + fQuitting = true; + write_port(fPort, 'quit', NULL, 0); + release_sem(fCursorSemaphore); +} + + +bool +InputServerStream::GetNextEvent(BMessage** _event) +{ + if (fEvents.IsEmpty()) { + // wait for new events + BMessage* event; + status_t status = _MessageFromPort(&event); + if (status == B_OK) + fEvents.AddMessage(event); + else if (status == B_BAD_PORT_ID) { + // our port got deleted - the input_server must have died + fPort = -1; + return false; + } + + int32 count = port_count(fPort); + if (count > 0) { + // empty port queue completely while we're at it + for (int32 i = 0; i < count; i++) { + if (_MessageFromPort(&event, 0) == B_OK) + fEvents.AddMessage(event); + } + } + } + + // there are items in our list, so just work through them + + *_event = fEvents.NextMessage(); + return true; +} + + +bool +InputServerStream::GetNextCursorPosition(BPoint &where) +{ + status_t status; + do { + status = acquire_sem(fCursorSemaphore); + } while (status == B_INTERRUPTED); + + if (status == B_BAD_SEM_ID) { + // the semaphore is no longer valid - the input_server must have died + fCursorSemaphore = -1; + return false; + } + + uint32 pos = atomic_get((int32*)&fCursorBuffer->pos); + where.x = pos & 0xffff; + where.y = pos >> 16L; + + atomic_and(&fCursorBuffer->read, 0); + // this tells the input_server that we've read the + // cursor position and want to be notified if updated + + if (fQuitting) { + fQuitting = false; + return false; + } + + return true; +} + + +status_t +InputServerStream::_MessageFromPort(BMessage** _message, bigtime_t timeout) +{ + uint8 *buffer = NULL; + ssize_t bufferSize; + + // read message from port + + do { + bufferSize = port_buffer_size_etc(fPort, B_RELATIVE_TIMEOUT, timeout); + } while (bufferSize == B_INTERRUPTED); + + if (bufferSize < B_OK) + return bufferSize; + + if (bufferSize > 0) { + buffer = new (std::nothrow) uint8[bufferSize]; + if (buffer == NULL) + return B_NO_MEMORY; + } + + int32 code; + bufferSize = read_port_etc(fPort, &code, buffer, bufferSize, + B_RELATIVE_TIMEOUT, 0); + if (bufferSize < B_OK) { + delete[] buffer; + return bufferSize; + } + + if (code == 'quit') { + // special code to tell our client to quit + BMessage* message = new BMessage(B_QUIT_REQUESTED); + if (message == NULL) + return B_NO_MEMORY; + + *_message = message; + return B_OK; + } + + // we have the message, now let's unflatten it + + BMessage* message = new BMessage(code); + if (message == NULL) + return B_NO_MEMORY; + + if (buffer == NULL) { + *_message = message; + return B_OK; + } + + status_t status = message->Unflatten((const char*)buffer); + delete[] buffer; + + if (status != B_OK) { + printf("Unflatten event failed: port message code was: %ld - %c%c%c%c\n", + code, (int8)(code >> 24), (int8)(code >> 16), (int8)(code >> 8), (int8)code); + delete message; + return status; + } + + *_message = message; + return B_OK; +} + diff --git a/src/servers/app/EventStream.h b/src/servers/app/EventStream.h new file mode 100644 index 0000000000..1f45114a2e --- /dev/null +++ b/src/servers/app/EventStream.h @@ -0,0 +1,57 @@ +/* + * Copyright 2005, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler, axeld@pinc-software.de + */ +#ifndef EVENT_STREAM_H +#define EVENT_STREAM_H + + +#include + +struct shared_cursor; + + +class EventStream { + public: + EventStream(); + virtual ~EventStream(); + + virtual bool IsValid() = 0; + virtual void SendQuit() = 0; + + virtual bool SupportsCursorThread() const; + + virtual bool GetNextEvent(BMessage** _event) = 0; + virtual bool GetNextCursorPosition(BPoint& where) = 0; +}; + + +class InputServerStream { + public: + InputServerStream(port_id port, port_id inputServerPort); + virtual ~InputServerStream(); + + virtual bool IsValid(); + virtual void SendQuit(); + + virtual bool SupportsCursorThread() const { return fCursorSemaphore >= B_OK; } + + virtual bool GetNextEvent(BMessage** _event); + virtual bool GetNextCursorPosition(BPoint& where); + + private: + status_t _MessageFromPort(BMessage** _message, + bigtime_t timeout = B_INFINITE_TIMEOUT); + + BMessageQueue fEvents; + port_id fPort; + bool fQuitting; + sem_id fCursorSemaphore; + area_id fCursorArea; + shared_cursor* fCursorBuffer; +}; + +#endif /* EVENT_STREAM_H */ diff --git a/src/servers/app/Jamfile b/src/servers/app/Jamfile index 095d16e3a5..7aceab8625 100644 --- a/src/servers/app/Jamfile +++ b/src/servers/app/Jamfile @@ -25,6 +25,8 @@ Server app_server : Desktop.cpp DesktopSettings.cpp DrawState.cpp + EventDispatcher.cpp + EventStream.cpp FontFamily.cpp FontManager.cpp HashTable.cpp