295 lines
5.3 KiB
C++
295 lines
5.3 KiB
C++
|
/*
|
||
|
* 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 <Autolock.h>
|
||
|
#include <MessageFilter.h>
|
||
|
#include <Messenger.h>
|
||
|
#include <View.h>
|
||
|
|
||
|
#include <stdio.h>
|
||
|
|
||
|
|
||
|
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;
|
||
|
}
|