1 /* 2 * Copyright 2005-2006, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler, axeld@pinc-software.de 7 */ 8 9 10 #include "EventDispatcher.h" 11 #include "EventStream.h" 12 #include "HWInterface.h" 13 #include "InputManager.h" 14 15 #include <TokenSpace.h> 16 17 #include <Autolock.h> 18 #include <View.h> 19 20 #include <new> 21 #include <stdio.h> 22 #include <string.h> 23 24 25 //#define TRACE_EVENTS 26 #ifdef TRACE_EVENTS 27 # define ETRACE(x) printf x 28 #else 29 # define ETRACE(x) ; 30 #endif 31 32 33 /*! 34 The EventDispatcher is a per Desktop object that handles all input 35 events for that desktop. 36 37 The events are processed as needed in the Desktop class (via EventFilters), 38 and then forwarded to the actual target of the event, a client window 39 (or more correctly, to its EventTarget). 40 You cannot set the target of an event directly - the event filters need 41 to specify the targets. 42 The event loop will make sure that every target and interested listener 43 will get the event - it also delivers mouse moved events to the previous 44 target once so that this target can then spread the B_EXITED_VIEW transit 45 to the local target handler (usually a BView). 46 47 If you look at the event_listener structure below, the differentiation 48 between target and token may look odd, but it really has a reason as 49 well: 50 All events are sent to the preferred window handler only - the window 51 may then use the token or token list to identify the specific target 52 view(s). This makes it possible to send every event only once, no 53 matter how many local target handlers there are. 54 */ 55 56 struct event_listener { 57 int32 token; 58 uint32 event_mask; 59 uint32 options; 60 uint32 temporary_event_mask; 61 uint32 temporary_options; 62 63 uint32 EffectiveEventMask() const { return event_mask | temporary_event_mask; } 64 uint32 EffectiveOptions() const { return options | temporary_options; } 65 }; 66 67 static const char* kTokenName = "_token"; 68 69 static const float kMouseMovedImportance = 0.1f; 70 static const float kMouseTransitImportance = 1.0f; 71 static const float kStandardImportance = 0.9f; 72 static const float kListenerImportance = 0.8f; 73 74 75 EventTarget::EventTarget() 76 : 77 fListeners(2, true) 78 { 79 } 80 81 82 EventTarget::~EventTarget() 83 { 84 } 85 86 87 void 88 EventTarget::SetTo(const BMessenger& messenger) 89 { 90 fMessenger = messenger; 91 } 92 93 94 event_listener* 95 EventTarget::FindListener(int32 token, int32* _index) 96 { 97 for (int32 i = fListeners.CountItems(); i-- > 0;) { 98 event_listener* listener = fListeners.ItemAt(i); 99 100 if (listener->token == token) { 101 if (_index) 102 *_index = i; 103 return listener; 104 } 105 } 106 107 return NULL; 108 } 109 110 111 bool 112 EventTarget::_RemoveTemporaryListener(event_listener* listener, int32 index) 113 { 114 if (listener->event_mask == 0) { 115 // this is only a temporary target 116 ETRACE(("events: remove temp. listener: token %ld, eventMask = %ld, options = %ld\n", 117 listener->token, listener->temporary_event_mask, listener->temporary_options)); 118 119 fListeners.RemoveItemAt(index); 120 delete listener; 121 return true; 122 } 123 124 if (listener->temporary_event_mask != 0) { 125 ETRACE(("events: clear temp. listener: token %ld, eventMask = %ld, options = %ld\n", 126 listener->token, listener->temporary_event_mask, listener->temporary_options)); 127 128 listener->temporary_event_mask = 0; 129 listener->temporary_options = 0; 130 } 131 132 return false; 133 } 134 135 136 void 137 EventTarget::RemoveTemporaryListeners() 138 { 139 for (int32 index = CountListeners(); index-- > 0;) { 140 event_listener* listener = ListenerAt(index); 141 142 _RemoveTemporaryListener(listener, index); 143 } 144 } 145 146 147 bool 148 EventTarget::RemoveTemporaryListener(int32 token) 149 { 150 int32 index; 151 event_listener* listener = FindListener(token, &index); 152 if (listener == NULL) 153 return false; 154 155 return _RemoveTemporaryListener(listener, index); 156 } 157 158 159 bool 160 EventTarget::RemoveListener(int32 token) 161 { 162 int32 index; 163 event_listener* listener = FindListener(token, &index); 164 if (listener == NULL) 165 return false; 166 167 if (listener->temporary_event_mask != 0) { 168 // we still need this event 169 listener->event_mask = 0; 170 listener->options = 0; 171 return false; 172 } 173 174 fListeners.RemoveItemAt(index); 175 delete listener; 176 return true; 177 } 178 179 180 bool 181 EventTarget::AddListener(int32 token, uint32 eventMask, 182 uint32 options, bool temporary) 183 { 184 event_listener* listener = new (std::nothrow) event_listener; 185 if (listener == NULL) 186 return false; 187 188 listener->token = token; 189 190 if (temporary) { 191 listener->event_mask = 0; 192 listener->options = 0; 193 listener->temporary_event_mask = eventMask; 194 listener->temporary_options = options; 195 } else { 196 listener->event_mask = eventMask; 197 listener->options = options; 198 listener->temporary_event_mask = 0; 199 listener->temporary_options = 0; 200 } 201 202 bool success = fListeners.AddItem(listener); 203 if (!success) 204 delete listener; 205 206 return success; 207 } 208 209 210 // #pragma mark - 211 212 213 EventDispatcher::EventDispatcher() 214 : BLocker("event dispatcher"), 215 fStream(NULL), 216 fThread(-1), 217 fCursorThread(-1), 218 fPreviousMouseTarget(NULL), 219 fPreviousViewToken(B_NULL_TOKEN), 220 fFocus(NULL), 221 fSuspendFocus(false), 222 fMouseFilter(NULL), 223 fKeyboardFilter(NULL), 224 fTargets(10), 225 fNextLatestMouseMoved(NULL), 226 fCursorLock("cursor loop lock"), 227 fHWInterface(NULL) 228 { 229 } 230 231 232 EventDispatcher::~EventDispatcher() 233 { 234 _Unset(); 235 } 236 237 238 status_t 239 EventDispatcher::SetTo(EventStream* stream) 240 { 241 ETRACE(("event dispatcher: stream = %p\n", stream)); 242 243 _Unset(); 244 245 if (stream == NULL) 246 return B_OK; 247 248 fStream = stream; 249 return _Run(); 250 } 251 252 253 status_t 254 EventDispatcher::InitCheck() 255 { 256 if (fStream != NULL) { 257 if (fThread < B_OK) 258 return fThread; 259 260 return B_OK; 261 } 262 return B_NO_INIT; 263 } 264 265 266 void 267 EventDispatcher::_Unset() 268 { 269 if (fStream == NULL) 270 return; 271 272 fStream->SendQuit(); 273 274 status_t status; 275 wait_for_thread(fThread, &status); 276 wait_for_thread(fCursorThread, &status); 277 278 fThread = fCursorThread = -1; 279 280 gInputManager->PutStream(fStream); 281 fStream = NULL; 282 } 283 284 285 status_t 286 EventDispatcher::_Run() 287 { 288 fThread = spawn_thread(_event_looper, "event loop", 289 B_REAL_TIME_DISPLAY_PRIORITY - 10, this); 290 if (fThread < B_OK) 291 return fThread; 292 293 if (fStream->SupportsCursorThread()) { 294 ETRACE(("event stream supports cursor thread!\n")); 295 296 fCursorThread = spawn_thread(_cursor_looper, "cursor loop", 297 B_REAL_TIME_DISPLAY_PRIORITY - 5, this); 298 if (resume_thread(fCursorThread) != B_OK) { 299 kill_thread(fCursorThread); 300 fCursorThread = -1; 301 } 302 } 303 304 return resume_thread(fThread); 305 } 306 307 308 /*! 309 \brief Removes any reference to the target, but doesn't delete it. 310 */ 311 void 312 EventDispatcher::RemoveTarget(EventTarget& target) 313 { 314 BAutolock _(this); 315 316 if (fFocus == &target) 317 fFocus = NULL; 318 if (fPreviousMouseTarget == &target) 319 fPreviousMouseTarget = NULL; 320 321 fTargets.RemoveItem(&target); 322 } 323 324 325 /*! 326 \brief Adds the specified listener or updates its event mask and options 327 if already added. 328 329 It follows the BView semantics in that specifiying an event mask of zero 330 leaves the event mask untouched and just updates the options. 331 */ 332 bool 333 EventDispatcher::_AddListener(EventTarget& target, int32 token, 334 uint32 eventMask, uint32 options, bool temporary) 335 { 336 BAutolock _(this); 337 338 if (!fTargets.HasItem(&target)) 339 fTargets.AddItem(&target); 340 341 event_listener* listener = target.FindListener(token); 342 if (listener != NULL) { 343 // we already have this target, update its event mask 344 if (temporary) { 345 if (eventMask != 0) 346 listener->temporary_event_mask = eventMask; 347 listener->temporary_options = options; 348 } else { 349 if (eventMask != 0) 350 listener->event_mask = eventMask; 351 listener->options = options; 352 } 353 354 return true; 355 } 356 357 if (eventMask == 0) 358 return false; 359 360 ETRACE(("events: add listener: token %ld, eventMask = %ld, options = %ld, %s\n", 361 token, eventMask, options, temporary ? "temporary" : "permanent")); 362 363 // we need a new target 364 365 bool success = target.AddListener(token, eventMask, options, temporary); 366 if (!success) { 367 if (target.IsEmpty()) 368 fTargets.RemoveItem(&target); 369 } else { 370 if (options & B_SUSPEND_VIEW_FOCUS) 371 fSuspendFocus = true; 372 } 373 374 return success; 375 } 376 377 378 void 379 EventDispatcher::_RemoveTemporaryListeners() 380 { 381 for (int32 i = fTargets.CountItems(); i-- > 0;) { 382 EventTarget* target = fTargets.ItemAt(i); 383 384 target->RemoveTemporaryListeners(); 385 } 386 } 387 388 389 bool 390 EventDispatcher::AddListener(EventTarget& target, int32 token, 391 uint32 eventMask, uint32 options) 392 { 393 options &= B_NO_POINTER_HISTORY; 394 // that's currently the only allowed option 395 396 return _AddListener(target, token, eventMask, options, false); 397 } 398 399 400 bool 401 EventDispatcher::AddTemporaryListener(EventTarget& target, 402 int32 token, uint32 eventMask, uint32 options) 403 { 404 return _AddListener(target, token, eventMask, options, true); 405 } 406 407 408 void 409 EventDispatcher::RemoveListener(EventTarget& target, int32 token) 410 { 411 BAutolock _(this); 412 ETRACE(("events: remove listener token %ld\n", token)); 413 414 if (target.RemoveListener(token) && target.IsEmpty()) 415 fTargets.RemoveItem(&target); 416 } 417 418 419 void 420 EventDispatcher::RemoveTemporaryListener(EventTarget& target, int32 token) 421 { 422 BAutolock _(this); 423 ETRACE(("events: remove temporary listener token %ld\n", token)); 424 425 if (target.RemoveTemporaryListener(token) && target.IsEmpty()) 426 fTargets.RemoveItem(&target); 427 } 428 429 430 void 431 EventDispatcher::SetMouseFilter(EventFilter* filter) 432 { 433 BAutolock _(this); 434 435 if (fMouseFilter == filter) 436 return; 437 438 delete fMouseFilter; 439 fMouseFilter = filter; 440 } 441 442 443 void 444 EventDispatcher::SetKeyboardFilter(EventFilter* filter) 445 { 446 BAutolock _(this); 447 448 if (fKeyboardFilter == filter) 449 return; 450 451 delete fKeyboardFilter; 452 fKeyboardFilter = filter; 453 } 454 455 456 void 457 EventDispatcher::GetMouse(BPoint& where, int32& buttons) 458 { 459 BAutolock _(this); 460 461 where = fLastCursorPosition; 462 buttons = fLastButtons; 463 } 464 465 466 void 467 EventDispatcher::SendFakeMouseMoved(EventTarget& target, int32 viewToken) 468 { 469 BAutolock _(this); 470 471 BMessage moved(B_MOUSE_MOVED); 472 moved.AddPoint("screen_where", fLastCursorPosition); 473 moved.AddInt32("buttons", fLastButtons); 474 moved.AddInt32("_view_token", viewToken); 475 476 if (fDraggingMessage) { 477 /* moved.AddInt32("_msg_data", ); 478 moved.AddInt32("_msg_base_", ); 479 moved.AddInt32("_msg_what_", fDragMessage->what);*/ 480 moved.AddMessage("be:drag_message", &fDragMessage); 481 } 482 483 _SendMessage(target.Messenger(), &moved, kMouseTransitImportance); 484 } 485 486 487 bool 488 EventDispatcher::HasCursorThread() 489 { 490 return fCursorThread >= B_OK; 491 } 492 493 494 /*! 495 \brief Sets the HWInterface to use when moving the mouse cursor. 496 \a interface is allowed to be NULL. 497 */ 498 void 499 EventDispatcher::SetHWInterface(HWInterface* interface) 500 { 501 BAutolock _(fCursorLock); 502 503 fHWInterface = interface; 504 } 505 506 507 int32 508 EventDispatcher::ViewUnderMouse(EventTarget& target) 509 { 510 BAutolock _(this); 511 512 if (&target == fPreviousMouseTarget) 513 return fPreviousViewToken; 514 515 return B_NULL_TOKEN; 516 } 517 518 519 void 520 EventDispatcher::SetDragMessage(BMessage& message) 521 { 522 printf("EventDispatcher::SetDragMessage()\n"); 523 524 BAutolock _(this); 525 526 fDragMessage = message; 527 528 fDraggingMessage = true; 529 } 530 531 // #pragma mark - Message methods 532 533 534 /*! 535 \brief Sends \a message to the provided \a messenger. 536 537 TODO: the following feature is not yet implemented: 538 If the message could not be delivered immediately, it is included 539 in a waiting message queue with a fixed length - the least important 540 messages are removed first when that gets full. 541 542 Returns "false" if the target port does not exist anymore, "true" 543 if it doesn't. 544 */ 545 bool 546 EventDispatcher::_SendMessage(BMessenger& messenger, BMessage* message, 547 float importance) 548 { 549 // TODO: add failed messages to a queue, and start dropping them by importance 550 // (and use the same mechanism in ServerWindow::SendMessageToClient()) 551 552 status_t status = messenger.SendMessage(message, (BHandler*)NULL, 0); 553 if (status != B_OK) { 554 printf("EventDispatcher: failed to send message '%.4s' to target: %s\n", 555 (char*)&message->what, strerror(status)); 556 } 557 558 if (status == B_BAD_PORT_ID) { 559 // the target port is gone 560 return false; 561 } 562 563 return true; 564 } 565 566 567 bool 568 EventDispatcher::_AddTokens(BMessage* message, EventTarget* target, 569 uint32 eventMask, BMessage* nextMouseMoved, int32* _viewToken) 570 { 571 _RemoveTokens(message); 572 573 int32 count = target->CountListeners(); 574 int32 added = 0; 575 576 for (int32 i = 0; i < count; i++) { 577 event_listener* listener = target->ListenerAt(i); 578 if ((listener->EffectiveEventMask() & eventMask) == 0) 579 continue; 580 581 if (nextMouseMoved != NULL && message->what == B_MOUSE_MOVED 582 && (listener->EffectiveOptions() & B_NO_POINTER_HISTORY) != 0 583 && message != nextMouseMoved 584 && _viewToken != NULL) { 585 if (listener->token == *_viewToken) { 586 // focus view doesn't want to get pointer history 587 *_viewToken = B_NULL_TOKEN; 588 } 589 continue; 590 } 591 592 ETRACE((" add token %ld\n", listener->token)); 593 594 if (message->AddInt32(kTokenName, listener->token) == B_OK) 595 added++; 596 } 597 598 return added != 0; 599 } 600 601 602 void 603 EventDispatcher::_RemoveTokens(BMessage* message) 604 { 605 message->RemoveName(kTokenName); 606 } 607 608 609 void 610 EventDispatcher::_SetFeedFocus(BMessage* message) 611 { 612 if (message->ReplaceBool("_feed_focus", true) != B_OK) 613 message->AddBool("_feed_focus", true); 614 } 615 616 617 void 618 EventDispatcher::_UnsetFeedFocus(BMessage* message) 619 { 620 message->RemoveName("_feed_focus"); 621 } 622 623 624 void 625 EventDispatcher::_DeliverDragMessage() 626 { 627 printf("EventDispatcher::_DeliverDragMessage()\n"); 628 629 if (fDraggingMessage && fPreviousMouseTarget) { 630 fDragMessage.RemoveName("_original_what"); 631 fDragMessage.AddInt32("_original_what", fDragMessage.what); 632 fDragMessage.what = _MESSAGE_DROPPED_; 633 634 // fDragMessage.AddBool("dropped", true); 635 printf(" sending message to previous mouse target\n"); 636 _SendMessage(fPreviousMouseTarget->Messenger(), 637 &fDragMessage, 100.0); 638 } 639 640 fDragMessage.MakeEmpty(); 641 fDragMessage.what = 0; 642 fDraggingMessage = false; 643 644 fHWInterface->SetDragBitmap(NULL, B_ORIGIN); 645 } 646 647 648 // #pragma mark - Event loops 649 650 651 void 652 EventDispatcher::_EventLoop() 653 { 654 BMessage* event; 655 while (fStream->GetNextEvent(&event)) { 656 if (event == NULL) { 657 // may happen in out of memory situations or junk at the port 658 // we can't do anything about those yet 659 continue; 660 } 661 662 BAutolock _(this); 663 664 EventTarget* current = NULL; 665 EventTarget* previous = NULL; 666 bool pointerEvent = false; 667 bool keyboardEvent = false; 668 bool addedTokens = false; 669 670 switch (event->what) { 671 case B_MOUSE_MOVED: 672 { 673 BPoint where; 674 if (event->FindPoint("where", &where) == B_OK) 675 fLastCursorPosition = where; 676 677 if (fDraggingMessage) { 678 /* event->AddInt32("_msg_data", ); 679 event->AddInt32("_msg_base_", ); 680 event->AddInt32("_msg_what_", fDragMessage->what);*/ 681 event->AddMessage("be:drag_message", &fDragMessage); 682 } 683 684 if (!HasCursorThread()) { 685 // there is no cursor thread, we need to move the cursor ourselves 686 BAutolock _(fCursorLock); 687 688 if (fHWInterface != NULL) { 689 fHWInterface->MoveCursorTo(fLastCursorPosition.x, 690 fLastCursorPosition.y); 691 } 692 } 693 694 // This is for B_NO_POINTER_HISTORY - we always want the 695 // latest mouse moved event in the queue only 696 if (fNextLatestMouseMoved == NULL) 697 fNextLatestMouseMoved = fStream->PeekLatestMouseMoved(); 698 else if (fNextLatestMouseMoved != event) { 699 // Drop older mouse moved messages if the server is lagging too 700 // much (if the message is older than 100 msecs) 701 bigtime_t eventTime; 702 if (event->FindInt64("when", &eventTime) == B_OK) { 703 if (system_time() - eventTime > 100000) 704 break; 705 } 706 } 707 708 // supposed to fall through 709 } 710 case B_MOUSE_DOWN: 711 case B_MOUSE_UP: 712 { 713 #ifdef TRACE_EVENTS 714 if (event->what != B_MOUSE_MOVED) 715 printf("mouse up/down event, previous target = %p\n", fPreviousMouseTarget); 716 #endif 717 pointerEvent = true; 718 719 if (fMouseFilter == NULL) 720 break; 721 722 EventTarget* mouseTarget = fPreviousMouseTarget; 723 int32 viewToken = B_NULL_TOKEN; 724 if (fMouseFilter->Filter(event, &mouseTarget, &viewToken, 725 fNextLatestMouseMoved) == B_SKIP_MESSAGE) { 726 // this is a work-around if the wrong B_MOUSE_UP 727 // event is filtered out 728 if (event->what == B_MOUSE_UP) { 729 fSuspendFocus = false; 730 _RemoveTemporaryListeners(); 731 } 732 break; 733 } 734 735 int32 buttons; 736 if (event->FindInt32("buttons", &buttons) == B_OK) 737 fLastButtons = buttons; 738 else 739 fLastButtons = 0; 740 741 // the "where" field will be filled in by the receiver 742 // (it's supposed to be expressed in local window coordinates) 743 event->RemoveName("where"); 744 event->AddPoint("screen_where", fLastCursorPosition); 745 746 if (event->what == B_MOUSE_MOVED 747 && fPreviousMouseTarget != NULL 748 && mouseTarget != fPreviousMouseTarget) { 749 // target has changed, we need to notify the previous target 750 // that the mouse has exited its views 751 addedTokens = _AddTokens(event, fPreviousMouseTarget, 752 B_POINTER_EVENTS); 753 754 _SendMessage(fPreviousMouseTarget->Messenger(), event, 755 kMouseTransitImportance); 756 previous = fPreviousMouseTarget; 757 } 758 759 current = fPreviousMouseTarget = mouseTarget; 760 fPreviousViewToken = viewToken; 761 762 if (current != NULL) { 763 int32 focusView = viewToken; 764 addedTokens |= _AddTokens(event, current, B_POINTER_EVENTS, 765 fNextLatestMouseMoved, &focusView); 766 767 bool noPointerHistoryFocus = focusView != viewToken; 768 769 if (viewToken != B_NULL_TOKEN) 770 event->AddInt32("_view_token", viewToken); 771 772 if (addedTokens && !noPointerHistoryFocus) 773 _SetFeedFocus(event); 774 else if (noPointerHistoryFocus) { 775 // no tokens were added or the focus shouldn't get a mouse moved 776 break; 777 } 778 779 _SendMessage(current->Messenger(), event, event->what == B_MOUSE_MOVED 780 ? kMouseMovedImportance : kStandardImportance); 781 } 782 break; 783 } 784 785 case B_KEY_DOWN: 786 case B_KEY_UP: 787 case B_UNMAPPED_KEY_DOWN: 788 case B_UNMAPPED_KEY_UP: 789 case B_MODIFIERS_CHANGED: 790 ETRACE(("key event, focus = %p\n", fFocus)); 791 792 if (fKeyboardFilter != NULL 793 && fKeyboardFilter->Filter(event, &fFocus) == B_SKIP_MESSAGE) 794 break; 795 796 keyboardEvent = true; 797 798 if (fFocus != NULL && _AddTokens(event, fFocus, B_KEYBOARD_EVENTS)) { 799 // if tokens were added, we need to explicetly suspend 800 // focus in the event - if not, the event is simply not 801 // forwarded to the target 802 addedTokens = true; 803 804 if (!fSuspendFocus) 805 _SetFeedFocus(event); 806 } 807 808 // supposed to fall through 809 810 default: 811 // TODO: the keyboard filter sets the focus - ie. no other 812 // focus messages that go through the event dispatcher can 813 // go through. 814 if (event->what == B_MOUSE_WHEEL_CHANGED) 815 current = fPreviousMouseTarget; 816 else 817 current = fFocus; 818 819 if (current != NULL && (!fSuspendFocus || addedTokens)) 820 _SendMessage(current->Messenger(), event, kStandardImportance); 821 break; 822 } 823 824 if (keyboardEvent || pointerEvent) { 825 // send the event to the additional listeners 826 827 if (addedTokens) { 828 _RemoveTokens(event); 829 _UnsetFeedFocus(event); 830 } 831 if (pointerEvent) { 832 // this is added in the Desktop mouse processing 833 // but it's only intended for the focus view 834 event->RemoveName("_view_token"); 835 } 836 837 for (int32 i = fTargets.CountItems(); i-- > 0;) { 838 EventTarget* target = fTargets.ItemAt(i); 839 840 // we already sent the event to the all focus and last focus tokens 841 if (current == target || previous == target) 842 continue; 843 844 // don't send the message if there are no tokens for this event 845 if (!_AddTokens(event, target, 846 keyboardEvent ? B_KEYBOARD_EVENTS : B_POINTER_EVENTS, 847 event->what == B_MOUSE_MOVED ? fNextLatestMouseMoved : NULL)) 848 continue; 849 850 if (!_SendMessage(target->Messenger(), event, event->what == B_MOUSE_MOVED 851 ? kMouseMovedImportance : kListenerImportance)) { 852 // the target doesn't seem to exist anymore, let's remove it 853 fTargets.RemoveItemAt(i); 854 } 855 } 856 857 if (event->what == B_MOUSE_UP) { 858 fSuspendFocus = false; 859 _RemoveTemporaryListeners(); 860 if (fDraggingMessage) 861 _DeliverDragMessage(); 862 } 863 } 864 865 if (fNextLatestMouseMoved == event) 866 fNextLatestMouseMoved = NULL; 867 delete event; 868 } 869 } 870 871 872 void 873 EventDispatcher::_CursorLoop() 874 { 875 BPoint where; 876 while (fStream->GetNextCursorPosition(where)) { 877 BAutolock _(fCursorLock); 878 879 if (fHWInterface != NULL) 880 fHWInterface->MoveCursorTo(where.x, where.y); 881 } 882 883 fCursorThread = -1; 884 } 885 886 887 /*static*/ 888 status_t 889 EventDispatcher::_event_looper(void* _dispatcher) 890 { 891 EventDispatcher* dispatcher = (EventDispatcher*)_dispatcher; 892 893 ETRACE(("Start event loop\n")); 894 dispatcher->_EventLoop(); 895 return B_OK; 896 } 897 898 899 /*static*/ 900 status_t 901 EventDispatcher::_cursor_looper(void* _dispatcher) 902 { 903 EventDispatcher* dispatcher = (EventDispatcher*)_dispatcher; 904 905 ETRACE(("Start cursor loop\n")); 906 dispatcher->_CursorLoop(); 907 return B_OK; 908 } 909