VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIMouseHandler.cpp@ 82781

Last change on this file since 82781 was 82032, checked in by vboxsync, 5 years ago

FE/Qt: bugref:9598: UISession, UIMachineView and UIMouseHandler: Now the curious one, mouse pointer shape scaling should be done on per-screen basis, so we had to move corresponding functionality from session to particular machine-view and keep proper signal order hierarchy accordingly; This will probably break frame-buffer cursor functionality.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 50.1 KB
Line 
1/* $Id: UIMouseHandler.cpp 82032 2019-11-20 16:21:59Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIMouseHandler class implementation.
4 */
5
6/*
7 * Copyright (C) 2010-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Qt includes: */
19#include <QMouseEvent>
20#include <QTimer>
21#include <QTouchEvent>
22#ifdef VBOX_WS_X11
23# include <QX11Info>
24#endif
25
26/* GUI includes: */
27#include "UICommon.h"
28#include "UIDesktopWidgetWatchdog.h"
29#include "UIExtraDataManager.h"
30#include "UIMessageCenter.h"
31#include "UIPopupCenter.h"
32#include "UISession.h"
33#include "UIMachineLogic.h"
34#include "UIMachineWindow.h"
35#include "UIMachineView.h"
36#include "UIKeyboardHandler.h"
37#include "UIMouseHandler.h"
38#include "UIFrameBuffer.h"
39#ifdef VBOX_WS_MAC
40# include "VBoxUtils-darwin.h"
41# include "CocoaEventHelper.h"
42#endif
43#ifdef VBOX_WS_WIN
44# include "VBoxUtils-win.h"
45#endif
46
47/* COM includes: */
48#include "CDisplay.h"
49#include "CMouse.h"
50
51/* Other VBox includes: */
52#include <iprt/time.h>
53
54/* External includes: */
55#ifdef VBOX_WS_X11
56#include "VBoxX11Helper.h"
57# include <xcb/xcb.h>
58#endif
59
60
61/* Factory function to create mouse-handler: */
62UIMouseHandler* UIMouseHandler::create(UIMachineLogic *pMachineLogic,
63 UIVisualStateType visualStateType)
64{
65 /* Prepare mouse-handler: */
66 UIMouseHandler *pMouseHandler = 0;
67 /* Depending on visual-state type: */
68 switch (visualStateType)
69 {
70 /* For now all the states using common mouse-handler: */
71 case UIVisualStateType_Normal:
72 case UIVisualStateType_Fullscreen:
73 case UIVisualStateType_Seamless:
74 case UIVisualStateType_Scale:
75 pMouseHandler = new UIMouseHandler(pMachineLogic);
76 break;
77 default:
78 break;
79 }
80 /* Return prepared mouse-handler: */
81 return pMouseHandler;
82}
83
84/* Factory function to destroy mouse-handler: */
85void UIMouseHandler::destroy(UIMouseHandler *pMouseHandler)
86{
87 /* Delete mouse-handler: */
88 delete pMouseHandler;
89}
90
91/* Prepare listener for particular machine-window: */
92void UIMouseHandler::prepareListener(ulong uIndex, UIMachineWindow *pMachineWindow)
93{
94 /* If that window is NOT registered yet: */
95 if (!m_windows.contains(uIndex))
96 {
97 /* Register machine-window: */
98 m_windows.insert(uIndex, pMachineWindow);
99 /* Install event-filter for machine-window: */
100 m_windows[uIndex]->installEventFilter(this);
101 }
102
103 /* If that view is NOT registered yet: */
104 if (!m_views.contains(uIndex))
105 {
106 /* Register machine-view: */
107 m_views.insert(uIndex, pMachineWindow->machineView());
108 /* Install event-filter for machine-view: */
109 m_views[uIndex]->installEventFilter(this);
110 /* Make machine-view notify mouse-handler about mouse pointer shape change: */
111 connect(m_views[uIndex], &UIMachineView::sigMousePointerShapeChange, this, &UIMouseHandler::sltMousePointerShapeChanged);
112 /* Make machine-view notify mouse-handler about frame-buffer resize: */
113 connect(m_views[uIndex], &UIMachineView::sigFrameBufferResize, this, &UIMouseHandler::sltMousePointerShapeChanged);
114 }
115
116 /* If that viewport is NOT registered yet: */
117 if (!m_viewports.contains(uIndex))
118 {
119 /* Register machine-view-viewport: */
120 m_viewports.insert(uIndex, pMachineWindow->machineView()->viewport());
121 /* Install event-filter for machine-view-viewport: */
122 m_viewports[uIndex]->installEventFilter(this);
123 }
124}
125
126/* Cleanup listener for particular machine-window: */
127void UIMouseHandler::cleanupListener(ulong uIndex)
128{
129 /* Check if we should release mouse first: */
130 if ((int)uIndex == m_iMouseCaptureViewIndex)
131 releaseMouse();
132
133 /* If that window still registered: */
134 if (m_windows.contains(uIndex))
135 {
136 /* Unregister machine-window: */
137 m_windows.remove(uIndex);
138 }
139
140 /* If that view still registered: */
141 if (m_views.contains(uIndex))
142 {
143 /* Unregister machine-view: */
144 m_views.remove(uIndex);
145 }
146
147 /* If that viewport still registered: */
148 if (m_viewports.contains(uIndex))
149 {
150 /* Unregister machine-view-viewport: */
151 m_viewports.remove(uIndex);
152 }
153}
154
155void UIMouseHandler::captureMouse(ulong uScreenId)
156{
157 /* Do not try to capture mouse if its captured already: */
158 if (uisession()->isMouseCaptured())
159 return;
160
161 /* If such viewport exists: */
162 if (m_viewports.contains(uScreenId))
163 {
164 /* Store mouse-capturing state value: */
165 uisession()->setMouseCaptured(true);
166
167 /* Memorize the index of machine-view-viewport captured mouse: */
168 m_iMouseCaptureViewIndex = uScreenId;
169
170 /* Memorize the host position where the cursor was captured: */
171 m_capturedMousePos = QCursor::pos();
172
173 /* Acquiring visible viewport rectangle in global coodrinates: */
174 QRect visibleRectangle = m_viewports[m_iMouseCaptureViewIndex]->visibleRegion().boundingRect();
175 QPoint visibleRectanglePos = m_views[m_iMouseCaptureViewIndex]->mapToGlobal(m_viewports[m_iMouseCaptureViewIndex]->pos());
176 visibleRectangle.translate(visibleRectanglePos);
177 visibleRectangle = visibleRectangle.intersected(gpDesktop->availableGeometry(machineLogic()->machineWindows()[m_iMouseCaptureViewIndex]));
178
179#ifdef VBOX_WS_WIN
180 /* Move the mouse to the center of the visible area: */
181 m_lastMousePos = visibleRectangle.center();
182 QCursor::setPos(m_lastMousePos);
183 /* Update mouse clipping: */
184 updateMouseCursorClipping();
185#elif defined (VBOX_WS_MAC)
186 /* Grab all mouse events: */
187 ::darwinMouseGrab(m_viewports[m_iMouseCaptureViewIndex]);
188#else /* VBOX_WS_MAC */
189 /* Remember current mouse position: */
190 m_lastMousePos = QCursor::pos();
191 /* Grab all mouse events: */
192 m_viewports[m_iMouseCaptureViewIndex]->grabMouse();
193#endif /* !VBOX_WS_MAC */
194
195 /* Switch guest mouse to the relative mode: */
196 mouse().PutMouseEvent(0, 0, 0, 0, 0);
197
198 /* Notify all the listeners: */
199 emit sigStateChange(state());
200 }
201}
202
203void UIMouseHandler::releaseMouse()
204{
205 /* Do not try to release mouse if its released already: */
206 if (!uisession()->isMouseCaptured())
207 return;
208
209 /* If such viewport exists: */
210 if (m_viewports.contains(m_iMouseCaptureViewIndex))
211 {
212 /* Store mouse-capturing state value: */
213 uisession()->setMouseCaptured(false);
214
215 /* Return the cursor to where it was when we captured it: */
216 QCursor::setPos(m_capturedMousePos);
217#ifdef VBOX_WS_WIN
218 /* Update mouse clipping: */
219 updateMouseCursorClipping();
220#elif defined(VBOX_WS_MAC)
221 /* Releasing grabbed mouse from that view: */
222 ::darwinMouseRelease(m_viewports[m_iMouseCaptureViewIndex]);
223#else /* VBOX_WS_MAC */
224 /* Releasing grabbed mouse from that view: */
225 m_viewports[m_iMouseCaptureViewIndex]->releaseMouse();
226#endif /* !VBOX_WS_MAC */
227 /* Reset mouse-capture index: */
228 m_iMouseCaptureViewIndex = -1;
229
230 /* Notify all the listeners: */
231 emit sigStateChange(state());
232 }
233}
234
235/* Setter for mouse-integration feature: */
236void UIMouseHandler::setMouseIntegrationEnabled(bool fEnabled)
237{
238 /* Do not do anything if its already done: */
239 if (uisession()->isMouseIntegrated() == fEnabled)
240 return;
241
242 /* Store mouse-integration state value: */
243 uisession()->setMouseIntegrated(fEnabled);
244
245 /* Reuse sltMouseCapabilityChanged() to update mouse state: */
246 sltMouseCapabilityChanged();
247}
248
249/* Current mouse state: */
250int UIMouseHandler::state() const
251{
252 return (uisession()->isMouseCaptured() ? UIMouseStateType_MouseCaptured : 0) |
253 (uisession()->isMouseSupportsAbsolute() ? UIMouseStateType_MouseAbsolute : 0) |
254 (uisession()->isMouseIntegrated() ? 0 : UIMouseStateType_MouseAbsoluteDisabled);
255}
256
257bool UIMouseHandler::nativeEventFilter(void *pMessage, ulong uScreenId)
258{
259 /* Make sure view with passed index exists: */
260 if (!m_views.contains(uScreenId))
261 return false;
262
263 /* Check if some system event should be filtered out.
264 * Returning @c true means filtering-out,
265 * Returning @c false means passing event to Qt. */
266 bool fResult = false; /* Pass to Qt by default. */
267
268# if defined(VBOX_WS_MAC)
269
270 /* Acquire carbon event reference from the cocoa one: */
271 EventRef event = static_cast<EventRef>(darwinCocoaToCarbonEvent(pMessage));
272
273 /* Depending on event kind: */
274 const UInt32 uEventKind = ::GetEventKind(event);
275 switch (uEventKind)
276 {
277 /* Watch for button-events: */
278 case kEventMouseDown:
279 case kEventMouseUp:
280 {
281 /* Acquire button number: */
282 EventMouseButton enmButton = 0;
283 ::GetEventParameter(event, kEventParamMouseButton, typeMouseButton,
284 NULL, sizeof(enmButton), NULL, &enmButton);
285 /* If the event comes for primary mouse button: */
286 if (enmButton == kEventMouseButtonPrimary)
287 {
288 /* Acquire modifiers: */
289 UInt32 uKeyModifiers = ~0U;
290 ::GetEventParameter(event, kEventParamKeyModifiers, typeUInt32,
291 NULL, sizeof(uKeyModifiers), NULL, &uKeyModifiers);
292 /* If the event comes with Control modifier: */
293 if (uKeyModifiers == controlKey)
294 {
295 /* Replacing it with the stripped one: */
296 darwinPostStrippedMouseEvent(pMessage);
297 /* And filter out initial one: */
298 return true;
299 }
300 }
301 }
302 }
303
304# elif defined(VBOX_WS_WIN)
305
306 /* Nothing for now. */
307 RT_NOREF(pMessage, uScreenId);
308
309# elif defined(VBOX_WS_X11)
310
311 /* Cast to XCB event: */
312 xcb_generic_event_t *pEvent = static_cast<xcb_generic_event_t*>(pMessage);
313
314 /* Depending on event type: */
315 switch (pEvent->response_type & ~0x80)
316 {
317 /* Watch for button-events: */
318 case XCB_BUTTON_PRESS:
319 {
320 /* Do nothing if mouse is actively grabbed: */
321 if (uisession()->isMouseCaptured())
322 break;
323
324 /* If we see a mouse press from a grab while the mouse is not captured,
325 * release the keyboard before letting the event owner see it. This is
326 * because some owners cannot deal with failures to grab the keyboard
327 * themselves (e.g. window managers dragging windows). */
328
329 /* Cast to XCB button-event: */
330 xcb_button_press_event_t *pButtonEvent = static_cast<xcb_button_press_event_t*>(pMessage);
331
332 /* If this event is from our button grab then it will be reported relative to the root
333 * window and not to ours. In that case release the keyboard capture, re-capture it
334 * delayed, which will fail if we have lost the input focus in the mean-time, replay
335 * the button event for normal delivery (possibly straight back to us, but not relative
336 * to root this time) and tell Qt not to further process this event: */
337 if (pButtonEvent->event == pButtonEvent->root)
338 {
339 machineLogic()->keyboardHandler()->releaseKeyboard();
340 /** @todo It would be nicer to do this in the normal Qt button event
341 * handler to avoid avoidable races if the event was not for us. */
342 machineLogic()->keyboardHandler()->captureKeyboard(uScreenId);
343 /* Re-send the event so that the window which it was meant for gets it: */
344 xcb_allow_events_checked(QX11Info::connection(), XCB_ALLOW_REPLAY_POINTER, pButtonEvent->time);
345 /* Do not let Qt see the event: */
346 return true;
347 }
348 }
349 default:
350 break;
351 }
352
353# else
354
355# warning "port me!"
356
357# endif
358
359 /* Return result: */
360 return fResult;
361}
362
363/* Machine state-change handler: */
364void UIMouseHandler::sltMachineStateChanged()
365{
366 /* Get machine state: */
367 KMachineState machineState = uisession()->machineState();
368 /* Handle particular machine states: */
369 switch (machineState)
370 {
371 case KMachineState_Paused:
372 case KMachineState_TeleportingPausedVM:
373 case KMachineState_Stuck:
374 {
375 /* Release the mouse: */
376 releaseMouse();
377 break;
378 }
379 default:
380 break;
381 }
382
383 /* Recall reminder about paused VM input
384 * if we are not in paused VM state already: */
385 if (machineLogic()->activeMachineWindow() &&
386 machineState != KMachineState_Paused &&
387 machineState != KMachineState_TeleportingPausedVM)
388 popupCenter().forgetAboutPausedVMInput(machineLogic()->activeMachineWindow());
389
390 /* Notify all the listeners: */
391 emit sigStateChange(state());
392}
393
394/* Mouse capability-change handler: */
395void UIMouseHandler::sltMouseCapabilityChanged()
396{
397 /* If mouse supports absolute pointing and mouse-integration activated: */
398 if (uisession()->isMouseSupportsAbsolute() && uisession()->isMouseIntegrated())
399 {
400 /* Release the mouse: */
401 releaseMouse();
402 /* Also we should switch guest mouse to the absolute mode: */
403 mouse().PutMouseEventAbsolute(-1, -1, 0, 0, 0);
404 }
405#if 0 /* current team's decision is NOT to capture mouse on mouse-absolute mode loosing! */
406 /* If mouse-integration deactivated or mouse doesn't supports absolute pointing: */
407 else
408 {
409 /* Search for the machine-view focused now: */
410 int iFocusedView = -1;
411 QList<ulong> screenIds = m_views.keys();
412 for (int i = 0; i < screenIds.size(); ++i)
413 {
414 if (m_views[screenIds[i]]->hasFocus())
415 {
416 iFocusedView = screenIds[i];
417 break;
418 }
419 }
420 /* If there is no focused view but views are present we will use the first one: */
421 if (iFocusedView == -1 && !screenIds.isEmpty())
422 iFocusedView = screenIds[0];
423 /* Capture mouse using that view: */
424 if (iFocusedView != -1)
425 captureMouse(iFocusedView);
426 }
427#else /* but just to switch the guest mouse into relative mode! */
428 /* If mouse-integration deactivated or mouse doesn't supports absolute pointing: */
429 else
430 {
431 /* Switch guest mouse to the relative mode: */
432 mouse().PutMouseEvent(0, 0, 0, 0, 0);
433 }
434#endif
435
436 /* Notify user about mouse supports or not absolute pointing if that method was called by signal: */
437 if (sender())
438 {
439 /* don't annoy the user while restoring a VM */
440 KMachineState state = uisession()->machineState();
441 if (state != KMachineState_Restoring)
442 popupCenter().remindAboutMouseIntegration(uisession()->machineLogic()->activeMachineWindow(),
443 uisession()->isMouseSupportsAbsolute());
444 }
445
446 /* Notify all the listeners: */
447 emit sigStateChange(state());
448}
449
450/* Mouse pointer-shape-change handler: */
451void UIMouseHandler::sltMousePointerShapeChanged()
452{
453 /* First of all, we should check if the host pointer should be visible.
454 * We should hide host pointer in case of:
455 * 1. mouse is 'captured' or
456 * 2. machine is NOT 'paused' and mouse is NOT 'captured' and 'integrated' and 'absolute' but host pointer is 'hidden' by the guest. */
457 if (uisession()->isMouseCaptured() ||
458 (!uisession()->isPaused() &&
459 uisession()->isMouseIntegrated() &&
460 uisession()->isMouseSupportsAbsolute() &&
461 uisession()->isHidingHostPointer()))
462 {
463 QList<ulong> screenIds = m_viewports.keys();
464 for (int i = 0; i < screenIds.size(); ++i)
465 UICommon::setCursor(m_viewports[screenIds[i]], Qt::BlankCursor);
466 }
467
468 else
469
470 /* Otherwise we should show host pointer with guest shape assigned to it if:
471 * machine is NOT 'paused', mouse is 'integrated' and 'absolute' and valid pointer shape is present. */
472 if (!uisession()->isPaused() &&
473 uisession()->isMouseIntegrated() &&
474 uisession()->isMouseSupportsAbsolute() &&
475 uisession()->isValidPointerShapePresent())
476 {
477 QList<ulong> screenIds = m_viewports.keys();
478 for (int i = 0; i < screenIds.size(); ++i)
479 UICommon::setCursor(m_viewports[screenIds[i]], m_views[screenIds[i]]->cursor());
480 }
481
482 else
483
484 /* There could be other states covering such situations as:
485 * 1. machine is 'paused' or
486 * 2. mouse is NOT 'captured' and 'integrated' but NOT 'absolute' or
487 * 3. mouse is NOT 'captured' and 'absolute' but NOT 'integrated'.
488 * We have nothing to do with that except just unset the cursor. */
489 {
490 QList<ulong> screenIds = m_viewports.keys();
491 for (int i = 0; i < screenIds.size(); ++i)
492 UICommon::unsetCursor(m_viewports[screenIds[i]]);
493 }
494}
495
496void UIMouseHandler::sltMaybeActivateHoveredWindow()
497{
498 /* Are we still have hovered window to activate? */
499 if (m_pHoveredWindow && !m_pHoveredWindow->isActiveWindow())
500 {
501 /* Activate it: */
502 m_pHoveredWindow->activateWindow();
503#ifdef VBOX_WS_X11
504 /* On X11 its not enough to just activate window if you
505 * want to raise it also, so we will make it separately: */
506 m_pHoveredWindow->raise();
507#endif /* VBOX_WS_X11 */
508 }
509}
510
511/* Mouse-handler constructor: */
512UIMouseHandler::UIMouseHandler(UIMachineLogic *pMachineLogic)
513 : QObject(pMachineLogic)
514 , m_pMachineLogic(pMachineLogic)
515 , m_iLastMouseWheelDelta(0)
516 , m_iMouseCaptureViewIndex(-1)
517#ifdef VBOX_WS_WIN
518 , m_fCursorPositionReseted(false)
519#endif
520{
521 /* Machine state-change updater: */
522 connect(uisession(), &UISession::sigMachineStateChange, this, &UIMouseHandler::sltMachineStateChanged);
523
524 /* Mouse capability state-change updater: */
525 connect(uisession(), &UISession::sigMouseCapabilityChange, this, &UIMouseHandler::sltMouseCapabilityChanged);
526
527 /* Mouse pointer shape state-change updater: */
528 connect(this, &UIMouseHandler::sigStateChange, this, &UIMouseHandler::sltMousePointerShapeChanged);
529
530 /* Mouse cursor position state-change updater: */
531 connect(uisession(), &UISession::sigCursorPositionChange, this, &UIMouseHandler::sltMousePointerShapeChanged);
532
533 /* Initialize: */
534 sltMachineStateChanged();
535 sltMousePointerShapeChanged();
536 sltMouseCapabilityChanged();
537}
538
539/* Mouse-handler destructor: */
540UIMouseHandler::~UIMouseHandler()
541{
542}
543
544/* Machine-logic getter: */
545UIMachineLogic* UIMouseHandler::machineLogic() const
546{
547 return m_pMachineLogic;
548}
549
550/* UI Session getter: */
551UISession* UIMouseHandler::uisession() const
552{
553 return machineLogic()->uisession();
554}
555
556CDisplay& UIMouseHandler::display() const
557{
558 return uisession()->display();
559}
560
561CMouse& UIMouseHandler::mouse() const
562{
563 return uisession()->mouse();
564}
565
566/* Event handler for registered machine-view(s): */
567bool UIMouseHandler::eventFilter(QObject *pWatched, QEvent *pEvent)
568{
569 /* If that object is of QWidget type: */
570 if (QWidget *pWatchedWidget = qobject_cast<QWidget*>(pWatched))
571 {
572 /* Check if that widget is in windows list: */
573 if (m_windows.values().contains(pWatchedWidget))
574 {
575#ifdef VBOX_WS_WIN
576 /* Handle window events: */
577 switch (pEvent->type())
578 {
579 case QEvent::Move:
580 {
581 /* Update mouse clipping if window was moved
582 * by Operating System desktop manager: */
583 updateMouseCursorClipping();
584 break;
585 }
586 default:
587 break;
588 }
589#endif /* VBOX_WS_WIN */
590 }
591
592 else
593
594 /* Check if that widget is of UIMachineView type: */
595 if (UIMachineView *pWatchedMachineView = qobject_cast<UIMachineView*>(pWatchedWidget))
596 {
597 /* Check if that widget is in views list: */
598 if (m_views.values().contains(pWatchedMachineView))
599 {
600 /* Handle view events: */
601 switch (pEvent->type())
602 {
603 case QEvent::FocusOut:
604 {
605 /* Release the mouse: */
606 releaseMouse();
607 break;
608 }
609 default:
610 break;
611 }
612 }
613 }
614
615 else
616
617 /* Check if that widget is in viewports list: */
618 if (m_viewports.values().contains(pWatchedWidget))
619 {
620 /* Get current watched widget screen id: */
621 ulong uScreenId = m_viewports.key(pWatchedWidget);
622 /* Handle viewport events: */
623 switch (pEvent->type())
624 {
625#ifdef VBOX_WS_MAC
626 case UIGrabMouseEvent::GrabMouseEvent:
627 {
628 UIGrabMouseEvent *pDeltaEvent = static_cast<UIGrabMouseEvent*>(pEvent);
629 QPoint p = QPoint(pDeltaEvent->xDelta() + m_lastMousePos.x(),
630 pDeltaEvent->yDelta() + m_lastMousePos.y());
631 if (mouseEvent(pDeltaEvent->mouseEventType(), uScreenId,
632 m_viewports[uScreenId]->mapFromGlobal(p), p,
633 pDeltaEvent->buttons(),
634 pDeltaEvent->wheelDelta(), pDeltaEvent->orientation()))
635 return true;
636 break;
637 }
638#endif /* VBOX_WS_MAC */
639 case QEvent::MouseMove:
640 {
641#ifdef VBOX_WS_MAC
642 // WORKAROUND:
643 // Since we are handling mouse move/drag events in the same thread
644 // where we are painting guest content changes corresponding to those
645 // events, we can have input event queue overloaded with the move/drag
646 // events, so we should discard current one if there is subsequent already.
647 EventTypeSpec list[2];
648 list[0].eventClass = kEventClassMouse;
649 list[0].eventKind = kEventMouseMoved;
650 list[1].eventClass = kEventClassMouse;
651 list[1].eventKind = kEventMouseDragged;
652 if (AcquireFirstMatchingEventInQueue(GetCurrentEventQueue(), 2, list,
653 kEventQueueOptionsNone) != NULL)
654 return true;
655#endif /* VBOX_WS_MAC */
656 }
657 case QEvent::MouseButtonRelease:
658 {
659 /* Get mouse-event: */
660 QMouseEvent *pOldMouseEvent = static_cast<QMouseEvent*>(pEvent);
661
662 /* Check which viewport(s) we *probably* hover: */
663 QWidgetList probablyHoveredViewports;
664 foreach (QWidget *pViewport, m_viewports)
665 {
666 QPoint posInViewport = pViewport->mapFromGlobal(pOldMouseEvent->globalPos());
667 if (pViewport->geometry().adjusted(0, 0, 1, 1).contains(posInViewport))
668 probablyHoveredViewports << pViewport;
669 }
670 /* Determine actually hovered viewport: */
671 QWidget *pHoveredWidget = probablyHoveredViewports.isEmpty() ? 0 :
672 probablyHoveredViewports.contains(pWatchedWidget) ? pWatchedWidget :
673 probablyHoveredViewports.first();
674
675 /* Check if we should propagate this event to another window: */
676 if (pHoveredWidget && pHoveredWidget != pWatchedWidget && m_viewports.values().contains(pHoveredWidget))
677 {
678 /* Prepare redirected mouse-move event: */
679 QMouseEvent *pNewMouseEvent = new QMouseEvent(pOldMouseEvent->type(),
680 pHoveredWidget->mapFromGlobal(pOldMouseEvent->globalPos()),
681 pOldMouseEvent->globalPos(),
682 pOldMouseEvent->button(),
683 pOldMouseEvent->buttons(),
684 pOldMouseEvent->modifiers());
685
686 /* Send that event to real destination: */
687 QApplication::postEvent(pHoveredWidget, pNewMouseEvent);
688
689 /* Filter out that event: */
690 return true;
691 }
692
693#ifdef VBOX_WS_X11
694 /* Make sure that we are focused after a click. Rather
695 * ugly, but works around a problem with GNOME
696 * screensaver, which sometimes removes our input focus
697 * and gives us no way to get it back. */
698 if (pEvent->type() == QEvent::MouseButtonRelease)
699 pWatchedWidget->window()->activateWindow();
700#endif /* VBOX_WS_X11 */
701 /* Check if we should activate window under cursor: */
702 if (gEDataManager->activateHoveredMachineWindow() &&
703 !uisession()->isMouseCaptured() &&
704 QApplication::activeWindow() &&
705 m_windows.values().contains(QApplication::activeWindow()) &&
706 m_windows.values().contains(pWatchedWidget->window()) &&
707 QApplication::activeWindow() != pWatchedWidget->window())
708 {
709 /* Put request for hovered window activation in 300msec: */
710 m_pHoveredWindow = pWatchedWidget->window();
711 QTimer::singleShot(300, this, SLOT(sltMaybeActivateHoveredWindow()));
712 }
713 else
714 {
715 /* Revoke request for hovered window activation: */
716 m_pHoveredWindow = 0;
717 }
718
719 /* This event should be also processed using next 'case': */
720 }
721 RT_FALL_THRU();
722 case QEvent::MouseButtonPress:
723 case QEvent::MouseButtonDblClick:
724 {
725 QMouseEvent *pMouseEvent = static_cast<QMouseEvent*>(pEvent);
726#ifdef VBOX_WS_X11
727 /* When the keyboard is captured, we also capture mouse button
728 * events, and release the keyboard and re-capture it delayed
729 * on every mouse click. When the click is inside our window
730 * area though the delay is not needed or wanted. Calling
731 * finaliseCaptureKeyboard() skips the delay if a delayed
732 * capture is in progress and has no effect if not: */
733 if (pEvent->type() == QEvent::MouseButtonPress)
734 machineLogic()->keyboardHandler()->finaliseCaptureKeyboard();
735#endif /* VBOX_WS_X11 */
736 m_iLastMouseWheelDelta = 0;
737 if (mouseEvent(pMouseEvent->type(), uScreenId,
738 pMouseEvent->pos(), pMouseEvent->globalPos(),
739 pMouseEvent->buttons(), 0, Qt::Horizontal))
740 return true;
741 break;
742 }
743 case QEvent::TouchBegin:
744 case QEvent::TouchUpdate:
745 case QEvent::TouchEnd:
746 {
747 if (uisession()->isMouseSupportsMultiTouch())
748 return multiTouchEvent(static_cast<QTouchEvent*>(pEvent), uScreenId);
749 break;
750 }
751 case QEvent::Wheel:
752 {
753 QWheelEvent *pWheelEvent = static_cast<QWheelEvent*>(pEvent);
754 /* There are pointing devices which send smaller values for the delta than 120.
755 * Here we sum them up until we are greater than 120. This allows to have finer control
756 * over the speed acceleration & enables such devices to send a valid wheel event to our
757 * guest mouse device at all: */
758 int iDelta = 0;
759 m_iLastMouseWheelDelta += pWheelEvent->delta();
760 if (qAbs(m_iLastMouseWheelDelta) >= 120)
761 {
762 iDelta = m_iLastMouseWheelDelta;
763 m_iLastMouseWheelDelta = m_iLastMouseWheelDelta % 120;
764 }
765 if (mouseEvent(pWheelEvent->type(), uScreenId,
766 pWheelEvent->pos(), pWheelEvent->globalPos(),
767#ifdef VBOX_WS_MAC
768 /* Qt Cocoa is buggy. It always reports a left button pressed when the
769 * mouse wheel event occurs. A workaround is to ask the application which
770 * buttons are pressed currently: */
771 QApplication::mouseButtons(),
772#else /* !VBOX_WS_MAC */
773 pWheelEvent->buttons(),
774#endif /* !VBOX_WS_MAC */
775 iDelta, pWheelEvent->orientation()))
776 return true;
777 break;
778 }
779#ifdef VBOX_WS_MAC
780 case QEvent::Leave:
781 {
782 /* Enable mouse event compression if we leave the VM view.
783 * This is necessary for having smooth resizing of the VM/other windows: */
784 ::darwinSetMouseCoalescingEnabled(true);
785 break;
786 }
787 case QEvent::Enter:
788 {
789 /* Disable mouse event compression if we enter the VM view.
790 * So all mouse events are registered in the VM.
791 * Only do this if the keyboard/mouse is grabbed
792 * (this is when we have a valid event handler): */
793 if (machineLogic()->keyboardHandler()->isKeyboardGrabbed())
794 darwinSetMouseCoalescingEnabled(false);
795 break;
796 }
797#endif /* VBOX_WS_MAC */
798#ifdef VBOX_WS_WIN
799 case QEvent::Resize:
800 {
801 /* Update mouse clipping: */
802 updateMouseCursorClipping();
803 break;
804 }
805#endif /* VBOX_WS_WIN */
806 default:
807 break;
808 }
809 }
810 }
811 return QObject::eventFilter(pWatched, pEvent);
812}
813
814/* Try to detect if the mouse event is fake and actually generated by a touch device. */
815#ifdef VBOX_WS_WIN
816#if (WINVER < 0x0601)
817typedef enum tagINPUT_MESSAGE_DEVICE_TYPE {
818 IMDT_UNAVAILABLE = 0, // 0x0
819 IMDT_KEYBOARD = 1, // 0x1
820 IMDT_MOUSE = 2, // 0x2
821 IMDT_TOUCH = 4, // 0x4
822 IMDT_PEN = 8 // 0x8
823} INPUT_MESSAGE_DEVICE_TYPE;
824
825typedef enum tagINPUT_MESSAGE_ORIGIN_ID {
826 IMO_UNAVAILABLE = 0x00000000,
827 IMO_HARDWARE = 0x00000001,
828 IMO_INJECTED = 0x00000002,
829 IMO_SYSTEM = 0x00000004
830} INPUT_MESSAGE_ORIGIN_ID;
831
832typedef struct tagINPUT_MESSAGE_SOURCE {
833 INPUT_MESSAGE_DEVICE_TYPE deviceType;
834 INPUT_MESSAGE_ORIGIN_ID originId;
835} INPUT_MESSAGE_SOURCE;
836#endif /* WINVER < 0x0601 */
837
838#define MOUSEEVENTF_FROMTOUCH 0xFF515700
839#define MOUSEEVENTF_MASK 0xFFFFFF00
840
841typedef BOOL WINAPI FNGetCurrentInputMessageSource(INPUT_MESSAGE_SOURCE *inputMessageSource);
842typedef FNGetCurrentInputMessageSource *PFNGetCurrentInputMessageSource;
843
844static bool mouseIsTouchSource(int iEventType, Qt::MouseButtons mouseButtons)
845{
846 NOREF(mouseButtons);
847
848 static PFNGetCurrentInputMessageSource pfnGetCurrentInputMessageSource = (PFNGetCurrentInputMessageSource)-1;
849 if (pfnGetCurrentInputMessageSource == (PFNGetCurrentInputMessageSource)-1)
850 {
851 HMODULE hUser = GetModuleHandle(L"user32.dll");
852 if (hUser)
853 pfnGetCurrentInputMessageSource =
854 (PFNGetCurrentInputMessageSource)GetProcAddress(hUser, "GetCurrentInputMessageSource");
855 }
856
857 int deviceType = -1;
858 if (pfnGetCurrentInputMessageSource)
859 {
860 INPUT_MESSAGE_SOURCE inputMessageSource;
861 BOOL fSuccess = pfnGetCurrentInputMessageSource(&inputMessageSource);
862 deviceType = fSuccess? inputMessageSource.deviceType: -2;
863 }
864 else
865 {
866 if ( iEventType == QEvent::MouseButtonPress
867 || iEventType == QEvent::MouseButtonRelease
868 || iEventType == QEvent::MouseMove)
869 deviceType = (GetMessageExtraInfo() & MOUSEEVENTF_MASK) == MOUSEEVENTF_FROMTOUCH? IMDT_TOUCH: -3;
870 }
871
872 LogRelFlow(("mouseIsTouchSource: deviceType %d\n", deviceType));
873 return deviceType == IMDT_TOUCH || deviceType == IMDT_PEN;
874}
875#else
876/* Apparently VBOX_WS_MAC does not generate fake mouse events.
877 * Other platforms, which have no known method to detect fake events are handled here too.
878 */
879static bool mouseIsTouchSource(int iEventType, Qt::MouseButtons mouseButtons)
880{
881 NOREF(iEventType);
882 NOREF(mouseButtons);
883 return false;
884}
885#endif
886
887/* Separate function to handle most of existing mouse-events: */
888bool UIMouseHandler::mouseEvent(int iEventType, ulong uScreenId,
889 const QPoint &relativePos, const QPoint &globalPos,
890 Qt::MouseButtons mouseButtons,
891 int wheelDelta, Qt::Orientation wheelDirection)
892{
893 /* Ignore fake mouse events. */
894 if (uisession()->isMouseSupportsMultiTouch() && mouseIsTouchSource(iEventType, mouseButtons))
895 return true;
896
897 /* Check if machine is still running: */
898 if (!uisession()->isRunning())
899 return true;
900
901 /* Check if such view & viewport are registered: */
902 if (!m_views.contains(uScreenId) || !m_viewports.contains(uScreenId))
903 return true;
904
905 int iMouseButtonsState = 0;
906 if (mouseButtons & Qt::LeftButton)
907 iMouseButtonsState |= KMouseButtonState_LeftButton;
908 if (mouseButtons & Qt::RightButton)
909 iMouseButtonsState |= KMouseButtonState_RightButton;
910 if (mouseButtons & Qt::MidButton)
911 iMouseButtonsState |= KMouseButtonState_MiddleButton;
912 if (mouseButtons & Qt::XButton1)
913 iMouseButtonsState |= KMouseButtonState_XButton1;
914 if (mouseButtons & Qt::XButton2)
915 iMouseButtonsState |= KMouseButtonState_XButton2;
916
917#ifdef VBOX_WS_MAC
918 /* Simulate the right click on host-key + left-mouse-button: */
919 if (machineLogic()->keyboardHandler()->isHostKeyPressed() &&
920 machineLogic()->keyboardHandler()->isHostKeyAlone() &&
921 iMouseButtonsState == KMouseButtonState_LeftButton)
922 iMouseButtonsState = KMouseButtonState_RightButton;
923#endif /* VBOX_WS_MAC */
924
925 int iWheelVertical = 0;
926 int iWheelHorizontal = 0;
927 if (wheelDirection == Qt::Vertical)
928 {
929 /* The absolute value of wheel delta is 120 units per every wheel move;
930 * positive deltas correspond to counterclockwise rotations (usually up),
931 * negative deltas correspond to clockwise (usually down). */
932 iWheelVertical = - (wheelDelta / 120);
933 }
934 else if (wheelDirection == Qt::Horizontal)
935 iWheelHorizontal = wheelDelta / 120;
936
937 if (uisession()->isMouseCaptured())
938 {
939#ifdef VBOX_WS_WIN
940 /* Send pending WM_PAINT events: */
941 ::UpdateWindow((HWND)m_viewports[uScreenId]->winId());
942#endif
943
944#ifdef VBOX_WS_WIN
945 // WORKAROUND:
946 // There are situations at least on Windows host that we are receiving
947 // previously posted (but not yet handled) mouse event right after we
948 // have manually teleported mouse cursor to simulate infinite movement,
949 // this makes cursor blink for a large amount of space, so we should
950 // ignore such blinks .. well, at least once.
951 const QPoint shiftingSpace = globalPos - m_lastMousePos;
952 if (m_fCursorPositionReseted && shiftingSpace.manhattanLength() >= 10)
953 {
954 m_fCursorPositionReseted = false;
955 return true;
956 }
957#endif
958
959 /* Pass event to the guest: */
960 mouse().PutMouseEvent(globalPos.x() - m_lastMousePos.x(),
961 globalPos.y() - m_lastMousePos.y(),
962 iWheelVertical, iWheelHorizontal, iMouseButtonsState);
963
964#ifdef VBOX_WS_WIN
965 /* Compose viewport-rectangle in local coordinates: */
966 QRect viewportRectangle = m_mouseCursorClippingRect;
967 QPoint viewportRectangleGlobalPos = m_views[uScreenId]->mapToGlobal(m_viewports[uScreenId]->pos());
968 viewportRectangle.translate(-viewportRectangleGlobalPos);
969
970 /* Compose boundaries: */
971 const int iX1 = viewportRectangle.left() + 1;
972 const int iY1 = viewportRectangle.top() + 1;
973 const int iX2 = viewportRectangle.right() - 1;
974 const int iY2 = viewportRectangle.bottom() - 1;
975
976 /* Simulate infinite movement: */
977 QPoint p = relativePos;
978 if (relativePos.x() <= iX1)
979 p.setX(iX2 - 1);
980 else if (relativePos.x() >= iX2)
981 p.setX(iX1 + 1);
982 if (relativePos.y() <= iY1)
983 p.setY(iY2 - 1);
984 else if (relativePos.y() >= iY2)
985 p.setY(iY1 + 1);
986 if (p != relativePos)
987 {
988 // WORKAROUND:
989 // Underlying QCursor::setPos call requires coordinates, rescaled according to primary screen.
990 // For that we have to map logical coordinates to relative origin (to make logical=>physical conversion).
991 // Besides that we have to make sure m_lastMousePos still uses logical coordinates afterwards.
992 const double dDprPrimary = gpDesktop->devicePixelRatio(gpDesktop->primaryScreen());
993 const double dDprCurrent = gpDesktop->devicePixelRatio(m_windows.value(m_iMouseCaptureViewIndex));
994 const QRect screenGeometry = gpDesktop->screenGeometry(m_windows.value(m_iMouseCaptureViewIndex));
995 QPoint requiredMousePos = (m_viewports[uScreenId]->mapToGlobal(p) - screenGeometry.topLeft()) * dDprCurrent + screenGeometry.topLeft();
996 QCursor::setPos(requiredMousePos / dDprPrimary);
997 m_lastMousePos = requiredMousePos / dDprCurrent;
998 m_fCursorPositionReseted = true;
999 }
1000 else
1001 {
1002 m_lastMousePos = globalPos;
1003 m_fCursorPositionReseted = false;
1004 }
1005#else /* VBOX_WS_WIN */
1006 int iWe = gpDesktop->overallDesktopWidth() - 1;
1007 int iHe = gpDesktop->overallDesktopHeight() - 1;
1008 QPoint p = globalPos;
1009 if (globalPos.x() == 0)
1010 p.setX(iWe - 1);
1011 else if (globalPos.x() == iWe)
1012 p.setX( 1 );
1013 if (globalPos.y() == 0)
1014 p.setY(iHe - 1);
1015 else if (globalPos.y() == iHe)
1016 p.setY(1);
1017
1018 if (p != globalPos)
1019 {
1020 m_lastMousePos = p;
1021 /* No need for cursor updating on the Mac, there is no one. */
1022# ifndef VBOX_WS_MAC
1023 QCursor::setPos(m_lastMousePos);
1024# endif /* VBOX_WS_MAC */
1025 }
1026 else
1027 m_lastMousePos = globalPos;
1028#endif /* !VBOX_WS_WIN */
1029 return true; /* stop further event handling */
1030 }
1031 else /* !uisession()->isMouseCaptured() */
1032 {
1033 if (uisession()->isMouseSupportsAbsolute() && uisession()->isMouseIntegrated())
1034 {
1035 int iCw = m_views[uScreenId]->contentsWidth(), iCh = m_views[uScreenId]->contentsHeight();
1036 int iVw = m_views[uScreenId]->visibleWidth(), iVh = m_views[uScreenId]->visibleHeight();
1037
1038 /* Try to automatically scroll the guest canvas if the
1039 * mouse goes outside its visible part: */
1040 int iDx = 0;
1041 if (relativePos.x() > iVw) iDx = relativePos.x() - iVw;
1042 else if (relativePos.x() < 0) iDx = relativePos.x();
1043 int iDy = 0;
1044 if (relativePos.y() > iVh) iDy = relativePos.y() - iVh;
1045 else if (relativePos.y() < 0) iDy = relativePos.y();
1046 if (iDx != 0 || iDy != 0) m_views[uScreenId]->scrollBy(iDx, iDy);
1047
1048 /* Get mouse-pointer location: */
1049 QPoint cpnt = m_views[uScreenId]->viewportToContents(relativePos);
1050
1051 /* Take the scale-factor(s) into account: */
1052 const UIFrameBuffer *pFrameBuffer = m_views[uScreenId]->frameBuffer();
1053 if (pFrameBuffer)
1054 {
1055 const QSize scaledSize = pFrameBuffer->scaledSize();
1056 if (scaledSize.isValid())
1057 {
1058 const double xScaleFactor = (double)scaledSize.width() / pFrameBuffer->width();
1059 const double yScaleFactor = (double)scaledSize.height() / pFrameBuffer->height();
1060 cpnt.setX((int)(cpnt.x() / xScaleFactor));
1061 cpnt.setY((int)(cpnt.y() / yScaleFactor));
1062 }
1063 }
1064
1065 /* Take the device-pixel-ratio into account: */
1066 const double dDevicePixelRatioFormal = pFrameBuffer->devicePixelRatio();
1067 const double dDevicePixelRatioActual = pFrameBuffer->devicePixelRatioActual();
1068 cpnt.setX(cpnt.x() * dDevicePixelRatioFormal);
1069 cpnt.setY(cpnt.y() * dDevicePixelRatioFormal);
1070 if (!pFrameBuffer->useUnscaledHiDPIOutput())
1071 {
1072 cpnt.setX(cpnt.x() / dDevicePixelRatioActual);
1073 cpnt.setY(cpnt.y() / dDevicePixelRatioActual);
1074 }
1075
1076#ifdef VBOX_WITH_DRAG_AND_DROP
1077# ifdef VBOX_WITH_DRAG_AND_DROP_GH
1078 QPointer<UIMachineView> pView = m_views[uScreenId];
1079 bool fHandleDnDPending = RT_BOOL(mouseButtons.testFlag(Qt::LeftButton));
1080
1081 /* Mouse pointer outside VM window? */
1082 if ( cpnt.x() < 0
1083 || cpnt.x() > iCw - 1
1084 || cpnt.y() < 0
1085 || cpnt.y() > iCh - 1)
1086 {
1087 if (fHandleDnDPending)
1088 {
1089 LogRel2(("DnD: Drag and drop operation from guest to host started\n"));
1090
1091 int rc = pView->dragCheckPending();
1092 if (RT_SUCCESS(rc))
1093 {
1094 pView->dragStart();
1095 return true; /* Bail out -- we're done here. */
1096 }
1097 }
1098 }
1099 else /* Inside VM window? */
1100 {
1101 if (fHandleDnDPending)
1102 pView->dragStop();
1103 }
1104# endif
1105#endif /* VBOX_WITH_DRAG_AND_DROP */
1106
1107 /* Bound coordinates: */
1108 if (cpnt.x() < 0) cpnt.setX(0);
1109 else if (cpnt.x() > iCw - 1) cpnt.setX(iCw - 1);
1110 if (cpnt.y() < 0) cpnt.setY(0);
1111 else if (cpnt.y() > iCh - 1) cpnt.setY(iCh - 1);
1112
1113 /* Determine shifting: */
1114 LONG xShift = 0, yShift = 0;
1115 ULONG dummy;
1116 KGuestMonitorStatus monitorStatus = KGuestMonitorStatus_Enabled;
1117 display().GetScreenResolution(uScreenId, dummy, dummy, dummy, xShift, yShift, monitorStatus);
1118 /* Set shifting: */
1119 cpnt.setX(cpnt.x() + xShift);
1120 cpnt.setY(cpnt.y() + yShift);
1121
1122 /* Post absolute mouse-event into guest: */
1123 mouse().PutMouseEventAbsolute(cpnt.x() + 1, cpnt.y() + 1, iWheelVertical, iWheelHorizontal, iMouseButtonsState);
1124 return true;
1125 }
1126 else
1127 {
1128 if (m_views[uScreenId]->hasFocus() && (iEventType == QEvent::MouseButtonRelease && mouseButtons == Qt::NoButton))
1129 {
1130 if (uisession()->isPaused())
1131 {
1132 popupCenter().remindAboutPausedVMInput(machineLogic()->activeMachineWindow());
1133 }
1134 else if (uisession()->isRunning())
1135 {
1136 /* Temporarily disable auto capture that will take place after this dialog is dismissed because
1137 * the capture state is to be defined by the dialog result itself: */
1138 uisession()->setAutoCaptureDisabled(true);
1139 bool fIsAutoConfirmed = false;
1140 bool ok = msgCenter().confirmInputCapture(fIsAutoConfirmed);
1141 if (fIsAutoConfirmed)
1142 uisession()->setAutoCaptureDisabled(false);
1143 /* Otherwise, the disable flag will be reset in the next console view's focus in event (since
1144 * may happen asynchronously on some platforms, after we return from this code): */
1145 if (ok)
1146 {
1147#ifdef VBOX_WS_X11
1148 /* Make sure that pending FocusOut events from the previous message box are handled,
1149 * otherwise the mouse is immediately ungrabbed again: */
1150 qApp->processEvents();
1151#endif /* VBOX_WS_X11 */
1152 machineLogic()->keyboardHandler()->captureKeyboard(uScreenId);
1153 const MouseCapturePolicy mcp = gEDataManager->mouseCapturePolicy(uiCommon().managedVMUuid());
1154 if (mcp == MouseCapturePolicy_Default)
1155 captureMouse(uScreenId);
1156 }
1157 }
1158 }
1159 }
1160 }
1161
1162 return false;
1163}
1164
1165bool UIMouseHandler::multiTouchEvent(QTouchEvent *pTouchEvent, ulong uScreenId)
1166{
1167 /* Eat if machine isn't running: */
1168 if (!uisession()->isRunning())
1169 return true;
1170
1171 /* Eat if such view & viewport aren't registered: */
1172 if (!m_views.contains(uScreenId) || !m_viewports.contains(uScreenId))
1173 return true;
1174
1175 QVector<LONG64> contacts(pTouchEvent->touchPoints().size());
1176
1177 LONG xShift = 0, yShift = 0;
1178 ULONG dummy;
1179 KGuestMonitorStatus monitorStatus = KGuestMonitorStatus_Enabled;
1180 display().GetScreenResolution(uScreenId, dummy, dummy, dummy, xShift, yShift, monitorStatus);
1181
1182 /* Pass all multi-touch events into guest: */
1183 int iTouchPointIndex = 0;
1184 foreach (const QTouchEvent::TouchPoint &touchPoint, pTouchEvent->touchPoints())
1185 {
1186 /* Get touch-point origin: */
1187 QPoint currentTouchPoint = touchPoint.pos().toPoint();
1188
1189 /* Get touch-point state: */
1190 LONG iTouchPointState = KTouchContactState_None;
1191 switch (touchPoint.state())
1192 {
1193 case Qt::TouchPointPressed:
1194 case Qt::TouchPointMoved:
1195 case Qt::TouchPointStationary:
1196 iTouchPointState = KTouchContactState_InContact | KTouchContactState_InRange;
1197 break;
1198 default:
1199 break;
1200 }
1201
1202 /* Pass absolute touch-point data: */
1203 LogRelFlow(("UIMouseHandler::multiTouchEvent: Origin: %dx%d, Id: %d, State: %d\n",
1204 currentTouchPoint.x(), currentTouchPoint.y(), touchPoint.id(), iTouchPointState));
1205
1206 contacts[iTouchPointIndex] = RT_MAKE_U64_FROM_U16((uint16_t)currentTouchPoint.x() + 1 + xShift,
1207 (uint16_t)currentTouchPoint.y() + 1 + yShift,
1208 RT_MAKE_U16(touchPoint.id(), iTouchPointState),
1209 0);
1210
1211 LogRelFlow(("UIMouseHandler::multiTouchEvent: %RX64\n", contacts[iTouchPointIndex]));
1212
1213 ++iTouchPointIndex;
1214 }
1215
1216 mouse().PutEventMultiTouch(pTouchEvent->touchPoints().size(),
1217 contacts,
1218 (ULONG)RTTimeMilliTS());
1219
1220 /* Eat by default? */
1221 return true;
1222}
1223
1224#ifdef VBOX_WS_WIN
1225/* This method is actually required only because under win-host
1226 * we do not really grab the mouse in case of capturing it: */
1227void UIMouseHandler::updateMouseCursorClipping()
1228{
1229 /* Check if such view && viewport are registered: */
1230 if (!m_views.contains(m_iMouseCaptureViewIndex) || !m_viewports.contains(m_iMouseCaptureViewIndex))
1231 return;
1232
1233 if (uisession()->isMouseCaptured())
1234 {
1235 /* Get full-viewport-rectangle in global coordinates: */
1236 QRect viewportRectangle = m_viewports[m_iMouseCaptureViewIndex]->visibleRegion().boundingRect();
1237 const QPoint viewportRectangleGlobalPos = m_views[m_iMouseCaptureViewIndex]->mapToGlobal(m_viewports[m_iMouseCaptureViewIndex]->pos());
1238 viewportRectangle.translate(viewportRectangleGlobalPos);
1239
1240 /* Trim full-viewport-rectangle by available geometry: */
1241 viewportRectangle = viewportRectangle.intersected(gpDesktop->availableGeometry(machineLogic()->machineWindows()[m_iMouseCaptureViewIndex]));
1242
1243 /* Trim partial-viewport-rectangle by top-most windows: */
1244 QRegion viewportRegion = QRegion(viewportRectangle) - NativeWindowSubsystem::areaCoveredByTopMostWindows();
1245 /* Check if partial-viewport-region consists of 1 rectangle: */
1246 if (viewportRegion.rectCount() > 1)
1247 {
1248 /* Choose the largest rectangle: */
1249 QRect largestRect;
1250 foreach (const QRect &rect, viewportRegion.rects())
1251 largestRect = largestRect.width() * largestRect.height() < rect.width() * rect.height() ? rect : largestRect;
1252 /* Assign the partial-viewport-region to the largest rect: */
1253 viewportRegion = largestRect;
1254 }
1255 /* Assign the partial-viewport-rectangle to the partial-viewport-region: */
1256 viewportRectangle = viewportRegion.boundingRect();
1257
1258 /* Assign the visible-viewport-rectangle to the partial-viewport-rectangle: */
1259 m_mouseCursorClippingRect = viewportRectangle;
1260
1261 /* Prepare clipping area: */
1262 // WORKAROUND:
1263 // Underlying ClipCursor call requires physical coordinates, not logical upscaled Qt stuff.
1264 // But we will have to map to relative origin (to make logical=>physical conversion) first.
1265 const double dDpr = gpDesktop->devicePixelRatio(m_windows.value(m_iMouseCaptureViewIndex));
1266 const QRect screenGeometry = gpDesktop->screenGeometry(m_windows.value(m_iMouseCaptureViewIndex));
1267 viewportRectangle.moveTo((viewportRectangle.topLeft() - screenGeometry.topLeft()) * dDpr + screenGeometry.topLeft());
1268 viewportRectangle.setSize(viewportRectangle.size() * dDpr);
1269 RECT rect = { viewportRectangle.left() + 1, viewportRectangle.top() + 1, viewportRectangle.right(), viewportRectangle.bottom() };
1270 ::ClipCursor(&rect);
1271 }
1272 else
1273 {
1274 ::ClipCursor(NULL);
1275 }
1276}
1277#endif /* VBOX_WS_WIN */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use