2 * Copyright 2005-2009, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
6 * Axel Dörfler, axeld@pinc-software.de
10 #include "EventDispatcher.h"
12 #include "BitmapManager.h"
14 #include "EventStream.h"
15 #include "HWInterface.h"
16 #include "InputManager.h"
17 #include "ServerBitmap.h"
19 #include <MessagePrivate.h>
20 #include <MessengerPrivate.h>
21 #include <ServerProtocol.h>
22 #include <TokenSpace.h>
25 #include <ToolTipManager.h>
33 //#define TRACE_EVENTS
35 # define ETRACE(x) printf x
42 The EventDispatcher is a per Desktop object that handles all input
43 events for that desktop.
45 The events are processed as needed in the Desktop class (via EventFilters),
46 and then forwarded to the actual target of the event, a client window
47 (or more correctly, to its EventTarget).
48 You cannot set the target of an event directly - the event filters need
49 to specify the targets.
50 The event loop will make sure that every target and interested listener
51 will get the event - it also delivers mouse moved events to the previous
52 target once so that this target can then spread the B_EXITED_VIEW transit
53 to the local target handler (usually a BView).
55 If you look at the event_listener structure below, the differentiation
56 between target and token may look odd, but it really has a reason as
58 All events are sent to the preferred window handler only - the window
59 may then use the token or token list to identify the specific target
60 view(s). This makes it possible to send every event only once, no
61 matter how many local target handlers there are.
64 struct event_listener
{
68 uint32 temporary_event_mask
;
69 uint32 temporary_options
;
71 uint32
EffectiveEventMask() const { return event_mask
| temporary_event_mask
; }
72 uint32
EffectiveOptions() const { return options
| temporary_options
; }
75 static const char* kTokenName
= "_token";
77 static const uint32 kFakeMouseMoved
= 'fake';
79 static const float kMouseMovedImportance
= 0.1f
;
80 static const float kMouseTransitImportance
= 1.0f
;
81 static const float kStandardImportance
= 0.9f
;
82 static const float kListenerImportance
= 0.8f
;
85 EventTarget::EventTarget()
92 EventTarget::~EventTarget()
98 EventTarget::SetTo(const BMessenger
& messenger
)
100 fMessenger
= messenger
;
105 EventTarget::FindListener(int32 token
, int32
* _index
)
107 for (int32 i
= fListeners
.CountItems(); i
-- > 0;) {
108 event_listener
* listener
= fListeners
.ItemAt(i
);
110 if (listener
->token
== token
) {
122 EventTarget::_RemoveTemporaryListener(event_listener
* listener
, int32 index
)
124 if (listener
->event_mask
== 0) {
125 // this is only a temporary target
126 ETRACE(("events: remove temp. listener: token %ld, eventMask = %ld, options = %ld\n",
127 listener
->token
, listener
->temporary_event_mask
, listener
->temporary_options
));
129 fListeners
.RemoveItemAt(index
);
134 if (listener
->temporary_event_mask
!= 0) {
135 ETRACE(("events: clear temp. listener: token %ld, eventMask = %ld, "
137 listener
->token
, listener
->temporary_event_mask
,
138 listener
->temporary_options
));
140 listener
->temporary_event_mask
= 0;
141 listener
->temporary_options
= 0;
149 EventTarget::RemoveTemporaryListeners()
151 for (int32 index
= CountListeners(); index
-- > 0;) {
152 event_listener
* listener
= ListenerAt(index
);
154 _RemoveTemporaryListener(listener
, index
);
160 EventTarget::RemoveTemporaryListener(int32 token
)
163 event_listener
* listener
= FindListener(token
, &index
);
164 if (listener
== NULL
)
167 return _RemoveTemporaryListener(listener
, index
);
172 EventTarget::RemoveListener(int32 token
)
175 event_listener
* listener
= FindListener(token
, &index
);
176 if (listener
== NULL
)
179 if (listener
->temporary_event_mask
!= 0) {
180 // we still need this event
181 listener
->event_mask
= 0;
182 listener
->options
= 0;
186 fListeners
.RemoveItemAt(index
);
193 EventTarget::AddListener(int32 token
, uint32 eventMask
,
194 uint32 options
, bool temporary
)
196 event_listener
* listener
= new (std::nothrow
) event_listener
;
197 if (listener
== NULL
)
200 listener
->token
= token
;
203 listener
->event_mask
= 0;
204 listener
->options
= 0;
205 listener
->temporary_event_mask
= eventMask
;
206 listener
->temporary_options
= options
;
208 listener
->event_mask
= eventMask
;
209 listener
->options
= options
;
210 listener
->temporary_event_mask
= 0;
211 listener
->temporary_options
= 0;
214 bool success
= fListeners
.AddItem(listener
);
226 EventFilter::RemoveTarget(EventTarget
* target
)
234 EventDispatcher::EventDispatcher()
236 BLocker("event dispatcher"),
240 fPreviousMouseTarget(NULL
),
242 fSuspendFocus(false),
244 fKeyboardFilter(NULL
),
246 fNextLatestMouseMoved(NULL
),
248 fLastUpdate(system_time()),
249 fDraggingMessage(false),
251 fCursorLock("cursor loop lock"),
258 EventDispatcher::~EventDispatcher()
265 EventDispatcher::SetTo(EventStream
* stream
)
267 ETRACE(("event dispatcher: stream = %p\n", stream
));
280 EventDispatcher::InitCheck()
282 if (fStream
!= NULL
) {
293 EventDispatcher::_Unset()
301 wait_for_thread(fThread
, &status
);
302 wait_for_thread(fCursorThread
, &status
);
304 fThread
= fCursorThread
= -1;
306 gInputManager
->PutStream(fStream
);
312 EventDispatcher::_Run()
314 fThread
= spawn_thread(_event_looper
, "event loop",
315 B_REAL_TIME_DISPLAY_PRIORITY
- 10, this);
319 if (fStream
->SupportsCursorThread()) {
320 ETRACE(("event stream supports cursor thread!\n"));
322 fCursorThread
= spawn_thread(_cursor_looper
, "cursor loop",
323 B_REAL_TIME_DISPLAY_PRIORITY
- 5, this);
324 if (resume_thread(fCursorThread
) != B_OK
) {
325 kill_thread(fCursorThread
);
330 return resume_thread(fThread
);
335 \brief Removes any reference to the target, but doesn't delete it.
338 EventDispatcher::RemoveTarget(EventTarget
& target
)
342 if (fFocus
== &target
)
344 if (fPreviousMouseTarget
== &target
)
345 fPreviousMouseTarget
= NULL
;
347 if (fKeyboardFilter
!= NULL
)
348 fKeyboardFilter
->RemoveTarget(&target
);
349 if (fMouseFilter
!= NULL
)
350 fMouseFilter
->RemoveTarget(&target
);
352 fTargets
.RemoveItem(&target
);
357 \brief Adds the specified listener or updates its event mask and options
360 It follows the BView semantics in that specifiying an event mask of zero
361 leaves the event mask untouched and just updates the options.
364 EventDispatcher::_AddListener(EventTarget
& target
, int32 token
,
365 uint32 eventMask
, uint32 options
, bool temporary
)
369 if (temporary
&& fLastButtons
== 0) {
370 // only allow to add temporary listeners in case a buttons is pressed
374 if (!fTargets
.HasItem(&target
))
375 fTargets
.AddItem(&target
);
377 event_listener
* listener
= target
.FindListener(token
);
378 if (listener
!= NULL
) {
379 // we already have this target, update its event mask
382 listener
->temporary_event_mask
= eventMask
;
383 listener
->temporary_options
= options
;
386 listener
->event_mask
= eventMask
;
387 listener
->options
= options
;
396 ETRACE(("events: add listener: token %ld, eventMask = %ld, options = %ld,"
398 token
, eventMask
, options
, temporary
? "temporary" : "permanent"));
400 // we need a new target
402 bool success
= target
.AddListener(token
, eventMask
, options
, temporary
);
404 if (target
.IsEmpty())
405 fTargets
.RemoveItem(&target
);
407 if (options
& B_SUSPEND_VIEW_FOCUS
)
408 fSuspendFocus
= true;
416 EventDispatcher::_RemoveTemporaryListeners()
418 for (int32 i
= fTargets
.CountItems(); i
-- > 0;) {
419 EventTarget
* target
= fTargets
.ItemAt(i
);
421 target
->RemoveTemporaryListeners();
427 EventDispatcher::AddListener(EventTarget
& target
, int32 token
,
428 uint32 eventMask
, uint32 options
)
430 options
&= B_NO_POINTER_HISTORY
;
431 // that's currently the only allowed option
433 return _AddListener(target
, token
, eventMask
, options
, false);
438 EventDispatcher::AddTemporaryListener(EventTarget
& target
,
439 int32 token
, uint32 eventMask
, uint32 options
)
441 return _AddListener(target
, token
, eventMask
, options
, true);
446 EventDispatcher::RemoveListener(EventTarget
& target
, int32 token
)
449 ETRACE(("events: remove listener token %ld\n", token
));
451 if (target
.RemoveListener(token
) && target
.IsEmpty())
452 fTargets
.RemoveItem(&target
);
457 EventDispatcher::RemoveTemporaryListener(EventTarget
& target
, int32 token
)
460 ETRACE(("events: remove temporary listener token %ld\n", token
));
462 if (target
.RemoveTemporaryListener(token
) && target
.IsEmpty())
463 fTargets
.RemoveItem(&target
);
468 EventDispatcher::SetMouseFilter(EventFilter
* filter
)
472 if (fMouseFilter
== filter
)
476 fMouseFilter
= filter
;
481 EventDispatcher::SetKeyboardFilter(EventFilter
* filter
)
485 if (fKeyboardFilter
== filter
)
488 delete fKeyboardFilter
;
489 fKeyboardFilter
= filter
;
494 EventDispatcher::GetMouse(BPoint
& where
, int32
& buttons
)
498 where
= fLastCursorPosition
;
499 buttons
= fLastButtons
;
504 EventDispatcher::SendFakeMouseMoved(EventTarget
& target
, int32 viewToken
)
509 BMessage
* fakeMove
= new BMessage(kFakeMouseMoved
);
510 if (fakeMove
== NULL
)
513 fakeMove
->AddMessenger("target", target
.Messenger());
514 fakeMove
->AddInt32("view_token", viewToken
);
516 fStream
->InsertEvent(fakeMove
);
521 EventDispatcher::_SendFakeMouseMoved(BMessage
* message
)
525 if (message
->FindInt32("view_token", &viewToken
) != B_OK
526 || message
->FindMessenger("target", &target
) != B_OK
)
529 if (fDesktop
== NULL
)
532 // Check if the target is still valid
533 ::EventTarget
* eventTarget
= NULL
;
535 fDesktop
->LockSingleWindow();
537 if (target
.IsValid())
538 eventTarget
= fDesktop
->FindTarget(target
);
540 fDesktop
->UnlockSingleWindow();
542 if (eventTarget
== NULL
)
545 BMessage
moved(B_MOUSE_MOVED
);
546 moved
.AddPoint("screen_where", fLastCursorPosition
);
547 moved
.AddInt32("buttons", fLastButtons
);
549 if (fDraggingMessage
)
550 moved
.AddMessage("be:drag_message", &fDragMessage
);
552 if (fPreviousMouseTarget
!= NULL
553 && fPreviousMouseTarget
->Messenger() != target
) {
554 _AddTokens(&moved
, fPreviousMouseTarget
, B_POINTER_EVENTS
);
555 _SendMessage(fPreviousMouseTarget
->Messenger(), &moved
,
556 kMouseTransitImportance
);
558 _RemoveTokens(&moved
);
561 moved
.AddInt32("_view_token", viewToken
);
562 // this only belongs to the new target
564 moved
.AddBool("be:transit_only", true);
565 // let the view know this what not user generated
567 _SendMessage(target
, &moved
, kMouseTransitImportance
);
569 fPreviousMouseTarget
= eventTarget
;
574 EventDispatcher::IdleTime()
577 return system_time() - fLastUpdate
;
582 EventDispatcher::HasCursorThread()
584 return fCursorThread
>= B_OK
;
589 \brief Sets the HWInterface to use when moving the mouse cursor.
590 \a interface is allowed to be NULL.
593 EventDispatcher::SetHWInterface(HWInterface
* interface
)
595 BAutolock
_(fCursorLock
);
597 fHWInterface
= interface
;
599 // adopt the cursor position of the new HW interface
600 if (interface
!= NULL
)
601 fLastCursorPosition
= interface
->CursorPosition();
606 EventDispatcher::SetDragMessage(BMessage
& message
,
607 ServerBitmap
* bitmap
, const BPoint
& offsetFromCursor
)
609 ETRACE(("EventDispatcher::SetDragMessage()\n"));
613 if (fLastButtons
== 0) {
614 // mouse buttons has already been released or was never pressed
616 bitmap
->ReleaseReference();
620 if (fDragBitmap
!= bitmap
) {
622 fDragBitmap
->ReleaseReference();
624 fDragBitmap
= bitmap
;
626 if (fDragBitmap
!= NULL
)
627 fDragBitmap
->AcquireReference();
630 fHWInterface
->SetDragBitmap(bitmap
, offsetFromCursor
);
632 fDragMessage
= message
;
633 fDraggingMessage
= true;
634 fDragOffset
= offsetFromCursor
;
639 EventDispatcher::SetDesktop(Desktop
* desktop
)
645 // #pragma mark - Message methods
649 \brief Sends \a message to the provided \a messenger.
651 TODO: the following feature is not yet implemented:
652 If the message could not be delivered immediately, it is included
653 in a waiting message queue with a fixed length - the least important
654 messages are removed first when that gets full.
656 Returns "false" if the target port does not exist anymore, "true"
660 EventDispatcher::_SendMessage(BMessenger
& messenger
, BMessage
* message
,
663 // TODO: add failed messages to a queue, and start dropping them by importance
664 // (and use the same mechanism in ServerWindow::SendMessageToClient())
666 status_t status
= messenger
.SendMessage(message
, (BHandler
*)NULL
, 0);
667 if (status
!= B_OK
) {
668 printf("EventDispatcher: failed to send message '%.4s' to target: %s\n",
669 (char*)&message
->what
, strerror(status
));
672 if (status
== B_BAD_PORT_ID
) {
673 // the target port is gone
682 EventDispatcher::_AddTokens(BMessage
* message
, EventTarget
* target
,
683 uint32 eventMask
, BMessage
* nextMouseMoved
, int32
* _viewToken
)
685 _RemoveTokens(message
);
687 int32 count
= target
->CountListeners();
690 for (int32 i
= 0; i
< count
; i
++) {
691 event_listener
* listener
= target
->ListenerAt(i
);
692 if ((listener
->EffectiveEventMask() & eventMask
) == 0)
695 if (nextMouseMoved
!= NULL
&& message
->what
== B_MOUSE_MOVED
696 && (listener
->EffectiveOptions() & B_NO_POINTER_HISTORY
) != 0
697 && message
!= nextMouseMoved
698 && _viewToken
!= NULL
) {
699 if (listener
->token
== *_viewToken
) {
700 // focus view doesn't want to get pointer history
701 *_viewToken
= B_NULL_TOKEN
;
706 ETRACE((" add token %ld\n", listener
->token
));
708 if (message
->AddInt32(kTokenName
, listener
->token
) == B_OK
)
717 EventDispatcher::_RemoveTokens(BMessage
* message
)
719 message
->RemoveName(kTokenName
);
724 EventDispatcher::_SetFeedFocus(BMessage
* message
)
726 if (message
->ReplaceBool("_feed_focus", true) != B_OK
)
727 message
->AddBool("_feed_focus", true);
732 EventDispatcher::_UnsetFeedFocus(BMessage
* message
)
734 message
->RemoveName("_feed_focus");
739 EventDispatcher::_DeliverDragMessage()
741 ETRACE(("EventDispatcher::_DeliverDragMessage()\n"));
743 if (fDraggingMessage
&& fPreviousMouseTarget
!= NULL
) {
744 BMessage::Private(fDragMessage
).SetWasDropped(true);
745 fDragMessage
.RemoveName("_original_what");
746 fDragMessage
.AddInt32("_original_what", fDragMessage
.what
);
747 fDragMessage
.AddPoint("_drop_point_", fLastCursorPosition
);
748 fDragMessage
.AddPoint("_drop_offset_", fDragOffset
);
749 fDragMessage
.what
= _MESSAGE_DROPPED_
;
751 _SendMessage(fPreviousMouseTarget
->Messenger(),
752 &fDragMessage
, 100.0);
755 fDragMessage
.MakeEmpty();
756 fDragMessage
.what
= 0;
757 fDraggingMessage
= false;
759 fHWInterface
->SetDragBitmap(NULL
, B_ORIGIN
);
760 if (fDragBitmap
!= NULL
) {
761 fDragBitmap
->ReleaseReference();
767 // #pragma mark - Event loops
771 EventDispatcher::_EventLoop()
774 while (fStream
->GetNextEvent(&event
)) {
776 fLastUpdate
= system_time();
778 EventTarget
* current
= NULL
;
779 EventTarget
* previous
= NULL
;
780 bool pointerEvent
= false;
781 bool keyboardEvent
= false;
782 bool addedTokens
= false;
784 switch (event
->what
) {
785 case kFakeMouseMoved
:
786 _SendFakeMouseMoved(event
);
791 if (event
->FindPoint("where", &where
) == B_OK
)
792 fLastCursorPosition
= where
;
794 if (fDraggingMessage
)
795 event
->AddMessage("be:drag_message", &fDragMessage
);
797 if (!HasCursorThread()) {
798 // There is no cursor thread, we need to move the cursor
800 BAutolock
_(fCursorLock
);
802 if (fHWInterface
!= NULL
) {
803 fHWInterface
->MoveCursorTo(fLastCursorPosition
.x
,
804 fLastCursorPosition
.y
);
808 // This is for B_NO_POINTER_HISTORY - we always want the
809 // latest mouse moved event in the queue only
810 if (fNextLatestMouseMoved
== NULL
)
811 fNextLatestMouseMoved
= fStream
->PeekLatestMouseMoved();
812 else if (fNextLatestMouseMoved
!= event
) {
813 // Drop older mouse moved messages if the server is lagging
814 // too much (if the message is older than 100 msecs)
816 if (event
->FindInt64("when", &eventTime
) == B_OK
) {
817 if (system_time() - eventTime
> 100000)
822 // supposed to fall through
829 if (event
->what
!= B_MOUSE_MOVED
)
830 printf("mouse up/down event, previous target = %p\n", fPreviousMouseTarget
);
834 if (fMouseFilter
== NULL
)
837 EventTarget
* mouseTarget
= fPreviousMouseTarget
;
838 int32 viewToken
= B_NULL_TOKEN
;
839 if (fMouseFilter
->Filter(event
, &mouseTarget
, &viewToken
,
840 fNextLatestMouseMoved
) == B_SKIP_MESSAGE
) {
841 // this is a work-around if the wrong B_MOUSE_UP
842 // event is filtered out
843 if (event
->what
== B_MOUSE_UP
844 && event
->FindInt32("buttons") == 0) {
845 fSuspendFocus
= false;
846 _RemoveTemporaryListeners();
852 if (event
->FindInt32("buttons", &buttons
) == B_OK
)
853 fLastButtons
= buttons
;
857 // The "where" field will be filled in by the receiver
858 // (it's supposed to be expressed in local window coordinates)
859 event
->RemoveName("where");
860 event
->AddPoint("screen_where", fLastCursorPosition
);
862 if (event
->what
== B_MOUSE_MOVED
863 && fPreviousMouseTarget
!= NULL
864 && mouseTarget
!= fPreviousMouseTarget
) {
865 // Target has changed, we need to notify the previous target
866 // that the mouse has exited its views
867 addedTokens
= _AddTokens(event
, fPreviousMouseTarget
,
870 _SetFeedFocus(event
);
872 _SendMessage(fPreviousMouseTarget
->Messenger(), event
,
873 kMouseTransitImportance
);
874 previous
= fPreviousMouseTarget
;
877 current
= fPreviousMouseTarget
= mouseTarget
;
879 if (current
!= NULL
) {
880 int32 focusView
= viewToken
;
881 addedTokens
|= _AddTokens(event
, current
, B_POINTER_EVENTS
,
882 fNextLatestMouseMoved
, &focusView
);
884 bool noPointerHistoryFocus
= focusView
!= viewToken
;
886 if (viewToken
!= B_NULL_TOKEN
)
887 event
->AddInt32("_view_token", viewToken
);
889 if (addedTokens
&& !noPointerHistoryFocus
)
890 _SetFeedFocus(event
);
891 else if (noPointerHistoryFocus
) {
892 // No tokens were added or the focus shouldn't get a
897 _SendMessage(current
->Messenger(), event
,
898 event
->what
== B_MOUSE_MOVED
899 ? kMouseMovedImportance
: kStandardImportance
);
906 case B_UNMAPPED_KEY_DOWN
:
907 case B_UNMAPPED_KEY_UP
:
908 case B_MODIFIERS_CHANGED
:
909 case B_INPUT_METHOD_EVENT
:
910 ETRACE(("key event, focus = %p\n", fFocus
));
912 if (fKeyboardFilter
!= NULL
913 && fKeyboardFilter
->Filter(event
, &fFocus
)
918 keyboardEvent
= true;
920 if (fFocus
!= NULL
&& _AddTokens(event
, fFocus
,
921 B_KEYBOARD_EVENTS
)) {
922 // if tokens were added, we need to explicetly suspend
923 // focus in the event - if not, the event is simply not
924 // forwarded to the target
928 _SetFeedFocus(event
);
931 // supposed to fall through
934 // TODO: the keyboard filter sets the focus - ie. no other
935 // focus messages that go through the event dispatcher can
937 if (event
->what
== B_MOUSE_WHEEL_CHANGED
)
938 current
= fPreviousMouseTarget
;
942 if (current
!= NULL
&& (!fSuspendFocus
|| addedTokens
)) {
943 _SendMessage(current
->Messenger(), event
,
944 kStandardImportance
);
949 if (keyboardEvent
|| pointerEvent
) {
950 // send the event to the additional listeners
953 _RemoveTokens(event
);
954 _UnsetFeedFocus(event
);
957 // this is added in the Desktop mouse processing
958 // but it's only intended for the focus view
959 event
->RemoveName("_view_token");
962 for (int32 i
= fTargets
.CountItems(); i
-- > 0;) {
963 EventTarget
* target
= fTargets
.ItemAt(i
);
965 // We already sent the event to the all focus and last focus
967 if (current
== target
|| previous
== target
)
970 // Don't send the message if there are no tokens for this event
971 if (!_AddTokens(event
, target
,
972 keyboardEvent
? B_KEYBOARD_EVENTS
: B_POINTER_EVENTS
,
973 event
->what
== B_MOUSE_MOVED
974 ? fNextLatestMouseMoved
: NULL
))
977 if (!_SendMessage(target
->Messenger(), event
,
978 event
->what
== B_MOUSE_MOVED
979 ? kMouseMovedImportance
: kListenerImportance
)) {
980 // the target doesn't seem to exist anymore, let's remove it
981 fTargets
.RemoveItemAt(i
);
985 if (event
->what
== B_MOUSE_UP
&& fLastButtons
== 0) {
986 // no buttons are pressed anymore
987 fSuspendFocus
= false;
988 _RemoveTemporaryListeners();
989 if (fDraggingMessage
)
990 _DeliverDragMessage();
994 if (fNextLatestMouseMoved
== event
)
995 fNextLatestMouseMoved
= NULL
;
999 // The loop quit, therefore no more events are coming from the input
1000 // server, it must have died. Unset ourselves and notify the desktop.
1002 // Needed to avoid problems with wait_for_thread in _Unset()
1006 fDesktop
->PostMessage(AS_EVENT_STREAM_CLOSED
);
1011 EventDispatcher::_CursorLoop()
1014 const bigtime_t toolTipDelay
= BToolTipManager::Manager()->ShowDelay();
1015 bool mouseIdleSent
= true;
1016 status_t status
= B_OK
;
1018 while (status
!= B_ERROR
) {
1019 const bigtime_t timeout
= mouseIdleSent
?
1020 B_INFINITE_TIMEOUT
: toolTipDelay
;
1021 status
= fStream
->GetNextCursorPosition(where
, timeout
);
1023 if (status
== B_OK
) {
1024 mouseIdleSent
= false;
1025 BAutolock
_(fCursorLock
);
1027 if (fHWInterface
!= NULL
)
1028 fHWInterface
->MoveCursorTo(where
.x
, where
.y
);
1029 } else if (status
== B_TIMED_OUT
) {
1030 mouseIdleSent
= true;
1031 BMessage
* mouseIdle
= new BMessage(B_MOUSE_IDLE
);
1032 mouseIdle
->AddPoint("screen_where", fLastCursorPosition
);
1033 fStream
->InsertEvent(mouseIdle
);
1043 EventDispatcher::_event_looper(void* _dispatcher
)
1045 EventDispatcher
* dispatcher
= (EventDispatcher
*)_dispatcher
;
1047 ETRACE(("Start event loop\n"));
1048 dispatcher
->_EventLoop();
1055 EventDispatcher::_cursor_looper(void* _dispatcher
)
1057 EventDispatcher
* dispatcher
= (EventDispatcher
*)_dispatcher
;
1059 ETRACE(("Start cursor loop\n"));
1060 dispatcher
->_CursorLoop();