VirtualBox

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

Last change on this file since 35509 was 35509, checked in by vboxsync, 13 years ago

FE/Qt: 5465: Mouse-cursor-shape should be updated on machine-state-change (for example, machine become paused/unpoused).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.7 KB
Line 
1/* $Id: UIMouseHandler.cpp 35509 2011-01-12 16:58:16Z vboxsync $ */
2/** @file
3 *
4 * VBox frontends: Qt GUI ("VirtualBox"):
5 * UIMouseHandler class implementation
6 */
7
8/*
9 * Copyright (C) 2010 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20/* Global includes */
21#include <QDesktopWidget>
22#include <QMouseEvent>
23
24/* Local includes */
25#include "VBoxGlobal.h"
26#include "VBoxProblemReporter.h"
27#include "UIKeyboardHandler.h"
28#include "UIMouseHandler.h"
29#include "UISession.h"
30#include "UIMachineLogic.h"
31#include "UIMachineWindow.h"
32#include "UIMachineView.h"
33#include "UIFrameBuffer.h"
34
35#ifdef Q_WS_X11
36# include <X11/XKBlib.h>
37# ifdef KeyPress
38const int XFocusOut = FocusOut;
39const int XFocusIn = FocusIn;
40const int XKeyPress = KeyPress;
41const int XKeyRelease = KeyRelease;
42# undef KeyRelease
43# undef KeyPress
44# undef FocusOut
45# undef FocusIn
46# endif /* KeyPress */
47#endif /* Q_WS_X11 */
48
49#ifdef Q_WS_MAC
50# include "VBoxUtils-darwin.h"
51#endif /* Q_WS_MAC */
52
53/* Factory function to create mouse-handler: */
54UIMouseHandler* UIMouseHandler::create(UIMachineLogic *pMachineLogic,
55 UIVisualStateType visualStateType)
56{
57 /* Prepare mouse-handler: */
58 UIMouseHandler *pMouseHandler = 0;
59 /* Depending on visual-state type: */
60 switch (visualStateType)
61 {
62 /* For now all the states using common mouse-handler: */
63 case UIVisualStateType_Normal:
64 case UIVisualStateType_Fullscreen:
65 case UIVisualStateType_Seamless:
66 case UIVisualStateType_Scale:
67 pMouseHandler = new UIMouseHandler(pMachineLogic);
68 break;
69 default:
70 break;
71 }
72 /* Return prepared mouse-handler: */
73 return pMouseHandler;
74}
75
76/* Factory function to destroy mouse-handler: */
77void UIMouseHandler::destroy(UIMouseHandler *pMouseHandler)
78{
79 /* Delete mouse-handler: */
80 delete pMouseHandler;
81}
82
83/* Prepare listener for particular machine-window: */
84void UIMouseHandler::prepareListener(ulong uIndex, UIMachineWindow *pMachineWindow)
85{
86 /* If that window is NOT registered yet: */
87 if (!m_windows.contains(uIndex))
88 {
89 /* Register machine-window: */
90 m_windows.insert(uIndex, pMachineWindow->machineWindow());
91 /* Install event-filter for machine-window: */
92 m_windows[uIndex]->installEventFilter(this);
93 }
94
95 /* If that view is NOT registered yet: */
96 if (!m_views.contains(uIndex))
97 {
98 /* Register machine-view: */
99 m_views.insert(uIndex, pMachineWindow->machineView());
100 /* Install event-filter for machine-view: */
101 m_views[uIndex]->installEventFilter(this);
102 /* Make machine-view notify mouse-handler about resizeHintDone(): */
103 connect(m_views[uIndex], SIGNAL(resizeHintDone()), this, SLOT(sltMousePointerShapeChanged()));
104 }
105
106 /* If that viewport is NOT registered yet: */
107 if (!m_viewports.contains(uIndex))
108 {
109 /* Register machine-view-viewport: */
110 m_viewports.insert(uIndex, pMachineWindow->machineView()->viewport());
111 /* Install event-filter for machine-view-viewport: */
112 m_viewports[uIndex]->installEventFilter(this);
113 }
114}
115
116/* Cleanup listener for particular machine-window: */
117void UIMouseHandler::cleanupListener(ulong uIndex)
118{
119 /* Check if we should release mouse first: */
120 if ((int)uIndex == m_iMouseCaptureViewIndex)
121 releaseMouse();
122
123 /* If that window still registered: */
124 if (m_windows.contains(uIndex))
125 {
126 /* Unregister machine-window: */
127 m_windows.remove(uIndex);
128 }
129
130 /* If that view still registered: */
131 if (m_views.contains(uIndex))
132 {
133 /* Unregister machine-view: */
134 m_views.remove(uIndex);
135 }
136
137 /* If that viewport still registered: */
138 if (m_viewports.contains(uIndex))
139 {
140 /* Unregister machine-view-viewport: */
141 m_viewports.remove(uIndex);
142 }
143}
144
145void UIMouseHandler::captureMouse(ulong uScreenId)
146{
147 /* Do not try to capture mouse if its captured already: */
148 if (uisession()->isMouseCaptured())
149 return;
150
151 /* If such viewport exists: */
152 if (m_viewports.contains(uScreenId))
153 {
154 /* Store mouse-capturing state value: */
155 uisession()->setMouseCaptured(true);
156
157 /* Memorize the index of machine-view-viewport captured mouse: */
158 m_iMouseCaptureViewIndex = uScreenId;
159
160 /* Memorize the host position where the cursor was captured: */
161 m_capturedMousePos = QCursor::pos();
162
163 /* Acquiring visible viewport rectangle in global coodrinates: */
164 QRect visibleRectangle = m_viewports[m_iMouseCaptureViewIndex]->visibleRegion().boundingRect();
165 QPoint visibleRectanglePos = m_views[m_iMouseCaptureViewIndex]->mapToGlobal(m_viewports[m_iMouseCaptureViewIndex]->pos());
166 visibleRectangle.translate(visibleRectanglePos);
167 visibleRectangle = visibleRectangle.intersected(QApplication::desktop()->availableGeometry());
168
169#ifdef Q_WS_WIN
170 /* Move the mouse to the center of the visible area: */
171 m_lastMousePos = visibleRectangle.center();
172 QCursor::setPos(m_lastMousePos);
173 /* Update mouse clipping: */
174 updateMouseCursorClipping();
175#elif defined (Q_WS_MAC)
176 /* Move the mouse to the center of the visible area: */
177 m_lastMousePos = visibleRectangle.center();
178 QCursor::setPos(m_lastMousePos);
179 /* Grab all mouse events: */
180 m_viewports[m_iMouseCaptureViewIndex]->grabMouse();
181#else /* Q_WS_MAC */
182 /* Remember current mouse position: */
183 m_lastMousePos = QCursor::pos();
184 /* Grab all mouse events: */
185 m_viewports[m_iMouseCaptureViewIndex]->grabMouse();
186#endif /* !Q_WS_MAC */
187
188 /* Switch guest mouse to the relative mode: */
189 CMouse mouse = session().GetConsole().GetMouse();
190 mouse.PutMouseEvent(0, 0, 0, 0, 0);
191
192 /* Emit signal if required: */
193 emit mouseStateChanged(mouseState());
194 }
195}
196
197void UIMouseHandler::releaseMouse()
198{
199 /* Do not try to release mouse if its released already: */
200 if (!uisession()->isMouseCaptured())
201 return;
202
203 /* If such viewport exists: */
204 if (m_viewports.contains(m_iMouseCaptureViewIndex))
205 {
206 /* Store mouse-capturing state value: */
207 uisession()->setMouseCaptured(false);
208
209 /* Return the cursor to where it was when we captured it: */
210 QCursor::setPos(m_capturedMousePos);
211#ifdef Q_WS_WIN
212 /* Update mouse clipping: */
213 updateMouseCursorClipping();
214#else /* Q_WS_WIN */
215 /* Releasing grabbed mouse from that view: */
216 m_viewports[m_iMouseCaptureViewIndex]->releaseMouse();
217#endif /* !Q_WS_WIN */
218 /* Reset mouse-capture index: */
219 m_iMouseCaptureViewIndex = -1;
220
221 /* Emit signal if required: */
222 emit mouseStateChanged(mouseState());
223 }
224}
225
226/* Setter for mouse-integration feature: */
227void UIMouseHandler::setMouseIntegrationEnabled(bool fEnabled)
228{
229 /* Do not do anything if its already done: */
230 if (uisession()->isMouseIntegrated() == fEnabled)
231 return;
232
233 /* Store mouse-integration state value: */
234 uisession()->setMouseIntegrated(fEnabled);
235
236 /* Reuse sltMouseCapabilityChanged() to update mouse state: */
237 sltMouseCapabilityChanged();
238}
239
240/* Current mouse state: */
241int UIMouseHandler::mouseState() const
242{
243 return (uisession()->isMouseCaptured() ? UIMouseStateType_MouseCaptured : 0) |
244 (uisession()->isMouseSupportsAbsolute() ? UIMouseStateType_MouseAbsolute : 0) |
245 (uisession()->isMouseIntegrated() ? 0 : UIMouseStateType_MouseAbsoluteDisabled);
246}
247
248#ifdef Q_WS_MAC
249void UIMouseHandler::setMouseCoalescingEnabled(bool fOn)
250{
251 /* Enable mouse event compression if we leave the VM view.
252 * This is necessary for having smooth resizing of the VM/other windows.
253 * Disable mouse event compression if we enter the VM view.
254 * So all mouse events are registered in the VM. */
255 ::darwinSetMouseCoalescingEnabled(fOn);
256}
257#endif /* Q_WS_MAC */
258
259#ifdef Q_WS_X11
260bool UIMouseHandler::x11EventFilter(XEvent *pEvent, ulong /* uScreenId */)
261{
262 /* Check if some system event should be filtered-out.
263 * Returning 'true' means filtering-out,
264 * Returning 'false' means passing event to Qt. */
265 bool fResult = false; /* Pass to Qt by default: */
266 switch (pEvent->type)
267 {
268 /* We have to handle XFocusOut right here as this event is not passed to UIMachineView::event().
269 * Handling this event is important for releasing the keyboard before the screen saver gets active.
270 * See public ticket #3894: Apparently this makes problems with newer versions of Qt
271 * and this hack is probably not necessary anymore. So disable it for Qt >= 4.5.0. */
272 case XFocusOut:
273 {
274 if (uisession()->isRunning())
275 {
276 if (VBoxGlobal::qtRTVersion() < ((4 << 16) | (5 << 8) | 0))
277 releaseMouse();
278 }
279 fResult = false;
280 }
281 default:
282 break;
283 }
284 /* Return result: */
285 return fResult;
286}
287#endif /* Q_WS_X11 */
288
289/* Machine state-change handler: */
290void UIMouseHandler::sltMachineStateChanged()
291{
292 /* Get machine state: */
293 KMachineState state = uisession()->machineState();
294 /* Handle particular machine states: */
295 switch (state)
296 {
297 case KMachineState_Paused:
298 case KMachineState_TeleportingPausedVM:
299 case KMachineState_Stuck:
300 {
301 /* Release the mouse: */
302 releaseMouse();
303 break;
304 }
305 default:
306 break;
307 }
308
309 /* Notify all listeners: */
310 emit mouseStateChanged(mouseState());
311}
312
313/* Mouse capability-change handler: */
314void UIMouseHandler::sltMouseCapabilityChanged()
315{
316 /* If mouse supports absolute pointing and mouse-integration activated: */
317 if (uisession()->isMouseSupportsAbsolute() && uisession()->isMouseIntegrated())
318 {
319 /* Release the mouse: */
320 releaseMouse();
321 /* Also we should switch guest mouse to the absolute mode: */
322 CMouse mouse = session().GetConsole().GetMouse();
323 mouse.PutMouseEventAbsolute(-1, -1, 0, 0, 0);
324 }
325#if 0 /* current team's decision is NOT to capture mouse on mouse-absolute mode loosing! */
326 /* If mouse-integration deactivated or mouse doesn't supports absolute pointing: */
327 else
328 {
329 /* Search for the machine-view focused now: */
330 int iFocusedView = -1;
331 QList<ulong> screenIds = m_views.keys();
332 for (int i = 0; i < screenIds.size(); ++i)
333 {
334 if (m_views[screenIds[i]]->hasFocus())
335 {
336 iFocusedView = screenIds[i];
337 break;
338 }
339 }
340 /* If there is no focused view but views are present we will use the first one: */
341 if (iFocusedView == -1 && !screenIds.isEmpty())
342 iFocusedView = screenIds[0];
343 /* Capture mouse using that view: */
344 if (iFocusedView != -1)
345 captureMouse(iFocusedView);
346 }
347#else /* but just to switch the guest mouse into relative mode! */
348 /* If mouse-integration deactivated or mouse doesn't supports absolute pointing: */
349 else
350 {
351 /* Switch guest mouse to the relative mode: */
352 CMouse mouse = session().GetConsole().GetMouse();
353 mouse.PutMouseEvent(0, 0, 0, 0, 0);
354 }
355#endif
356
357 /* Notify user about mouse supports or not absolute pointing if that method was called by signal: */
358 if (sender())
359 vboxProblem().remindAboutMouseIntegration(uisession()->isMouseSupportsAbsolute());
360
361 /* Notify all listeners: */
362 emit mouseStateChanged(mouseState());
363}
364
365/* Mouse pointer-shape-change handler: */
366void UIMouseHandler::sltMousePointerShapeChanged()
367{
368 /* First of all, we should check if the host pointer should be visible.
369 * We should hide host pointer in case of:
370 * 1. mouse is 'captured' or
371 * 2. mouse is 'not captured', 'integrated', 'absolute', host pointer is hidden by the guest. */
372 if (uisession()->isMouseCaptured() ||
373 (uisession()->isMouseIntegrated() &&
374 uisession()->isMouseSupportsAbsolute() &&
375 uisession()->isHidingHostPointer()))
376 {
377 QList<ulong> screenIds = m_viewports.keys();
378 for (int i = 0; i < screenIds.size(); ++i)
379 m_viewports[screenIds[i]]->setCursor(Qt::BlankCursor);
380 }
381
382 else
383
384 /* Otherwise we should show host pointer with guest shape assigned to it if:
385 * machine is NOT 'paused', mouse is 'integrated', 'absolute', valid pointer shape is present. */
386 if (!uisession()->isPaused() &&
387 uisession()->isMouseIntegrated() &&
388 uisession()->isMouseSupportsAbsolute() &&
389 uisession()->isValidPointerShapePresent())
390 {
391 QList<ulong> screenIds = m_viewports.keys();
392 for (int i = 0; i < screenIds.size(); ++i)
393 m_viewports[screenIds[i]]->setCursor(uisession()->cursor());
394 }
395
396 else
397
398 /* There could be other states covering such situations as:
399 * 1. machine is 'paused' or
400 * 2. mouse is 'not captured', 'integrated', 'not absolute' or
401 * 3. mouse is 'not captured', 'not integrated', 'absolute'.
402 * We have nothing to do with that except just unset the cursor. */
403 {
404 QList<ulong> screenIds = m_viewports.keys();
405 for (int i = 0; i < screenIds.size(); ++i)
406 m_viewports[screenIds[i]]->unsetCursor();
407 }
408}
409
410/* Mouse-handler constructor: */
411UIMouseHandler::UIMouseHandler(UIMachineLogic *pMachineLogic)
412 : QObject(pMachineLogic)
413 , m_pMachineLogic(pMachineLogic)
414 , m_iLastMouseWheelDelta(0)
415 , m_iMouseCaptureViewIndex(-1)
416{
417 /* Machine state-change updater: */
418 connect(uisession(), SIGNAL(sigMachineStateChange()), this, SLOT(sltMachineStateChanged()));
419
420 /* Mouse capability state-change updater: */
421 connect(uisession(), SIGNAL(sigMouseCapabilityChange()), this, SLOT(sltMouseCapabilityChanged()));
422
423 /* Mouse pointer shape state-change updaters: */
424 connect(uisession(), SIGNAL(sigMousePointerShapeChange()), this, SLOT(sltMousePointerShapeChanged()));
425 connect(this, SIGNAL(mouseStateChanged(int)), this, SLOT(sltMousePointerShapeChanged()));
426
427 /* Initialize: */
428 sltMachineStateChanged();
429 sltMousePointerShapeChanged();
430 sltMouseCapabilityChanged();
431}
432
433/* Mouse-handler destructor: */
434UIMouseHandler::~UIMouseHandler()
435{
436}
437
438/* Machine-logic getter: */
439UIMachineLogic* UIMouseHandler::machineLogic() const
440{
441 return m_pMachineLogic;
442}
443
444/* UI Session getter: */
445UISession* UIMouseHandler::uisession() const
446{
447 return machineLogic()->uisession();
448}
449
450/* Main Session getter: */
451CSession& UIMouseHandler::session() const
452{
453 return uisession()->session();
454}
455
456/* Event handler for registered machine-view(s): */
457bool UIMouseHandler::eventFilter(QObject *pWatched, QEvent *pEvent)
458{
459 /* If that object is of QWidget type: */
460 if (QWidget *pWatchedWidget = qobject_cast<QWidget*>(pWatched))
461 {
462 /* Check if that widget is in windows list: */
463 if (m_windows.values().contains(pWatchedWidget))
464 {
465#ifdef Q_WS_WIN
466 /* Handle window events: */
467 switch (pEvent->type())
468 {
469 case QEvent::Move:
470 {
471 /* Update mouse clipping if window was moved
472 * by Operating System desktop manager: */
473 updateMouseCursorClipping();
474 break;
475 }
476 default:
477 break;
478 }
479#endif /* Q_WS_WIN */
480 }
481
482 else
483
484 /* Check if that widget is of UIMachineView type: */
485 if (UIMachineView *pWatchedMachineView = qobject_cast<UIMachineView*>(pWatchedWidget))
486 {
487 /* Check if that widget is in views list: */
488 if (m_views.values().contains(pWatchedMachineView))
489 {
490 /* Handle view events: */
491 switch (pEvent->type())
492 {
493 case QEvent::FocusOut:
494 {
495 /* Release the mouse: */
496 releaseMouse();
497 break;
498 }
499 default:
500 break;
501 }
502 }
503 }
504
505 else
506
507 /* Check if that widget is in viewports list: */
508 if (m_viewports.values().contains(pWatchedWidget))
509 {
510 /* Get current watched widget screen id: */
511 ulong uScreenId = m_viewports.key(pWatchedWidget);
512 /* Handle viewport events: */
513 switch (pEvent->type())
514 {
515 case QEvent::MouseMove:
516 case QEvent::MouseButtonRelease:
517 {
518 /* Check if we should propagate this event to another window: */
519 QWidget *pHoveredWidget = QApplication::widgetAt(QCursor::pos());
520 if (pHoveredWidget && pHoveredWidget != pWatchedWidget && m_viewports.values().contains(pHoveredWidget))
521 {
522 /* Get current mouse-move event: */
523 QMouseEvent *pOldMouseEvent = static_cast<QMouseEvent*>(pEvent);
524
525 /* Prepare redirected mouse-move event: */
526 QMouseEvent *pNewMouseEvent = new QMouseEvent(pOldMouseEvent->type(),
527 pHoveredWidget->mapFromGlobal(pOldMouseEvent->globalPos()),
528 pOldMouseEvent->globalPos(),
529 pOldMouseEvent->button(),
530 pOldMouseEvent->buttons(),
531 pOldMouseEvent->modifiers());
532
533 /* Send that event to real destination: */
534 QApplication::postEvent(pHoveredWidget, pNewMouseEvent);
535
536 /* Filter out that event: */
537 return true;
538 }
539
540 /* Check if we should activate window under cursor: */
541 if (!uisession()->isMouseCaptured() &&
542 QApplication::activeWindow() &&
543 QApplication::activeWindow()->inherits("UIMachineWindow") &&
544 QApplication::activeWindow() != pWatchedWidget->window())
545 {
546 /* Activating hovered machine window: */
547 pWatchedWidget->window()->activateWindow();
548#ifdef Q_WS_X11
549 /* On X11 its not enough to just activate window if you
550 * want to raise it also, so we will make it separately: */
551 pWatchedWidget->window()->raise();
552#endif /* Q_WS_X11 */
553 }
554
555 /* This event should be also processed using next 'case': */
556 }
557 case QEvent::MouseButtonPress:
558 case QEvent::MouseButtonDblClick:
559 {
560 QMouseEvent *pMouseEvent = static_cast<QMouseEvent*>(pEvent);
561 m_iLastMouseWheelDelta = 0;
562 if (mouseEvent(pMouseEvent->type(), uScreenId,
563 pMouseEvent->pos(), pMouseEvent->globalPos(),
564 pMouseEvent->buttons(), 0, Qt::Horizontal))
565 return true;
566 break;
567 }
568 case QEvent::Wheel:
569 {
570 QWheelEvent *pWheelEvent = static_cast<QWheelEvent*>(pEvent);
571 /* There are pointing devices which send smaller values for the delta than 120.
572 * Here we sum them up until we are greater than 120. This allows to have finer control
573 * over the speed acceleration & enables such devices to send a valid wheel event to our
574 * guest mouse device at all: */
575 int iDelta = 0;
576 m_iLastMouseWheelDelta += pWheelEvent->delta();
577 if (qAbs(m_iLastMouseWheelDelta) >= 120)
578 {
579 iDelta = m_iLastMouseWheelDelta;
580 m_iLastMouseWheelDelta = m_iLastMouseWheelDelta % 120;
581 }
582 if (mouseEvent(pWheelEvent->type(), uScreenId,
583 pWheelEvent->pos(), pWheelEvent->globalPos(),
584#ifdef QT_MAC_USE_COCOA
585 /* Qt Cocoa is buggy. It always reports a left button pressed when the
586 * mouse wheel event occurs. A workaround is to ask the application which
587 * buttons are pressed currently: */
588 QApplication::mouseButtons(),
589#else /* QT_MAC_USE_COCOA */
590 pWheelEvent->buttons(),
591#endif /* !QT_MAC_USE_COCOA */
592 iDelta, pWheelEvent->orientation()))
593 return true;
594 break;
595 }
596#ifdef Q_WS_MAC
597 case QEvent::Leave:
598 {
599 /* Enable mouse event compression if we leave the VM view.
600 * This is necessary for having smooth resizing of the VM/other windows: */
601 setMouseCoalescingEnabled(true);
602 break;
603 }
604 case QEvent::Enter:
605 {
606 /* Disable mouse event compression if we enter the VM view.
607 * So all mouse events are registered in the VM.
608 * Only do this if the keyboard/mouse is grabbed
609 * (this is when we have a valid event handler): */
610 if (machineLogic()->keyboardHandler()->isKeyboardGrabbed())
611 setMouseCoalescingEnabled(false);
612 break;
613 }
614#endif /* Q_WS_MAC */
615#ifdef Q_WS_WIN
616 case QEvent::Resize:
617 {
618 /* Update mouse clipping: */
619 updateMouseCursorClipping();
620 break;
621 }
622#endif /* Q_WS_WIN */
623 default:
624 break;
625 }
626 }
627 }
628 return QObject::eventFilter(pWatched, pEvent);
629}
630
631/* Separate function to handle most of existing mouse-events: */
632bool UIMouseHandler::mouseEvent(int iEventType, ulong uScreenId,
633 const QPoint &relativePos, const QPoint &globalPos,
634 Qt::MouseButtons mouseButtons,
635 int wheelDelta, Qt::Orientation wheelDirection)
636{
637 /* Check if machine is still running: */
638 if (!uisession()->isRunning())
639 return true;
640
641 /* Check if such view & viewport are registered: */
642 if (!m_views.contains(uScreenId) || !m_viewports.contains(uScreenId))
643 return true;
644
645 int iMouseButtonsState = 0;
646 if (mouseButtons & Qt::LeftButton)
647 iMouseButtonsState |= KMouseButtonState_LeftButton;
648 if (mouseButtons & Qt::RightButton)
649 iMouseButtonsState |= KMouseButtonState_RightButton;
650 if (mouseButtons & Qt::MidButton)
651 iMouseButtonsState |= KMouseButtonState_MiddleButton;
652 if (mouseButtons & Qt::XButton1)
653 iMouseButtonsState |= KMouseButtonState_XButton1;
654 if (mouseButtons & Qt::XButton2)
655 iMouseButtonsState |= KMouseButtonState_XButton2;
656
657#ifdef Q_WS_MAC
658 /* Simulate the right click on host-key + left-mouse-button: */
659 if (machineLogic()->keyboardHandler()->isHostKeyPressed() &&
660 machineLogic()->keyboardHandler()->isHostKeyAlone() &&
661 iMouseButtonsState == KMouseButtonState_LeftButton)
662 iMouseButtonsState = KMouseButtonState_RightButton;
663#endif /* Q_WS_MAC */
664
665 int iWheelVertical = 0;
666 int iWheelHorizontal = 0;
667 if (wheelDirection == Qt::Vertical)
668 {
669 /* The absolute value of wheel delta is 120 units per every wheel move;
670 * positive deltas correspond to counterclockwise rotations (usually up),
671 * negative deltas correspond to clockwise (usually down). */
672 iWheelVertical = - (wheelDelta / 120);
673 }
674 else if (wheelDirection == Qt::Horizontal)
675 iWheelHorizontal = wheelDelta / 120;
676
677 if (uisession()->isMouseCaptured())
678 {
679#ifdef Q_WS_WIN
680 /* Send pending WM_PAINT events: */
681 ::UpdateWindow(m_viewports[uScreenId]->winId());
682#endif
683
684 CMouse mouse = session().GetConsole().GetMouse();
685 mouse.PutMouseEvent(globalPos.x() - m_lastMousePos.x(),
686 globalPos.y() - m_lastMousePos.y(),
687 iWheelVertical, iWheelHorizontal, iMouseButtonsState);
688
689#ifdef Q_WS_MAC
690 /* Keep the mouse from leaving the widget.
691 * This is a bit tricky to get right because if it escapes we won't necessarily
692 * get mouse events any longer and can warp it back. So, we keep safety zone
693 * of up to 300 pixels around the borders of the widget to prevent this from
694 * happening. Also, the mouse is warped back to the center of the widget.
695 * (Note, relativePos seems to be unreliable, it caused endless recursion here at one points...)
696 * (Note, synergy and other remote clients might not like this cursor warping.) */
697 QRect rect = m_viewports[uScreenId]->visibleRegion().boundingRect();
698 QPoint pw = m_viewports[uScreenId]->mapToGlobal(m_viewports[uScreenId]->pos());
699 rect.translate(pw.x(), pw.y());
700
701 QRect dpRect = QApplication::desktop()->screenGeometry(m_viewports[uScreenId]);
702 if (rect.intersects(dpRect))
703 rect = rect.intersect(dpRect);
704
705 int iWsafe = rect.width() / 6;
706 rect.setWidth(rect.width() - iWsafe * 2);
707 rect.setLeft(rect.left() + iWsafe);
708
709 int iHsafe = rect.height() / 6;
710 rect.setWidth(rect.height() - iHsafe * 2);
711 rect.setTop(rect.top() + iHsafe);
712
713 if (rect.contains(globalPos, true))
714 m_lastMousePos = globalPos;
715 else
716 {
717 m_lastMousePos = rect.center();
718 QCursor::setPos(m_lastMousePos);
719 }
720#else /* Q_WS_MAC */
721
722 /* Bringing mouse to the opposite side to simulate the endless moving: */
723
724# ifdef Q_WS_WIN
725 /* Acquiring visible viewport rectangle in local coordinates: */
726 QRect viewportRectangle = m_viewports[uScreenId]->visibleRegion().boundingRect();
727 QPoint viewportRectangleGlobalPos = m_views[uScreenId]->mapToGlobal(m_viewports[uScreenId]->pos());
728 /* Shift viewport rectangle to global position to bound by available geometry: */
729 viewportRectangle.translate(viewportRectangleGlobalPos);
730 /* Acquiring viewport rectangle cropped by available geometry: */
731 viewportRectangle = viewportRectangle.intersected(QApplication::desktop()->availableGeometry());
732 /* Shift remaining viewport rectangle to local position as relative position is in local coordinates: */
733 viewportRectangle.translate(-viewportRectangleGlobalPos);
734
735 /* Get boundaries: */
736 int ix1 = viewportRectangle.left() + 1;
737 int iy1 = viewportRectangle.top() + 1;
738 int ix2 = viewportRectangle.right() - 1;
739 int iy2 = viewportRectangle.bottom() - 1;
740
741 /* Simulate infinite movement: */
742 QPoint p = relativePos;
743 if (relativePos.x() == ix1)
744 p.setX(ix2 - 1);
745 else if (relativePos.x() == ix2)
746 p.setX(ix1 + 1);
747 if (relativePos.y() == iy1)
748 p.setY(iy2 - 1);
749 else if (relativePos.y() == iy2)
750 p.setY(iy1 + 1);
751 if (p != relativePos)
752 {
753 m_lastMousePos = m_viewports[uScreenId]->mapToGlobal(p);
754 QCursor::setPos(m_lastMousePos);
755 }
756 else
757 {
758 m_lastMousePos = globalPos;
759 }
760# else /* Q_WS_WIN */
761 int iWe = QApplication::desktop()->width() - 1;
762 int iHe = QApplication::desktop()->height() - 1;
763 QPoint p = globalPos;
764 if (globalPos.x() == 0)
765 p.setX(iWe - 1);
766 else if (globalPos.x() == iWe)
767 p.setX( 1 );
768 if (globalPos.y() == 0)
769 p.setY(iHe - 1);
770 else if (globalPos.y() == iHe)
771 p.setY(1);
772
773 if (p != globalPos)
774 {
775 m_lastMousePos = p;
776 QCursor::setPos(m_lastMousePos);
777 }
778 else
779 {
780 m_lastMousePos = globalPos;
781 }
782# endif /* !Q_WS_WIN */
783#endif /* !Q_WS_MAC */
784 return true; /* stop further event handling */
785 }
786 else /* !uisession()->isMouseCaptured() */
787 {
788#if 0 // TODO: Move that to fullscreen event-handler:
789 if (vboxGlobal().vmRenderMode() != VBoxDefs::SDLMode)
790 {
791 /* try to automatically scroll the guest canvas if the
792 * mouse is on the screen border */
793 /// @todo (r=dmik) better use a timer for autoscroll
794 QRect scrGeo = QApplication::desktop()->screenGeometry (this);
795 int iDx = 0, iDy = 0;
796 if (scrGeo.width() < contentsWidth())
797 {
798 if (scrGeo.left() == globalPos.x()) iDx = -1;
799 if (scrGeo.right() == globalPos.x()) iDx = +1;
800 }
801 if (scrGeo.height() < contentsHeight())
802 {
803 if (scrGeo.top() == globalPos.y()) iDy = -1;
804 if (scrGeo.bottom() == globalPos.y()) iDy = +1;
805 }
806 if (iDx || iDy)
807 scrollBy(iDx, iDy);
808 }
809#endif
810
811 if (uisession()->isMouseSupportsAbsolute() && uisession()->isMouseIntegrated())
812 {
813 int iCw = m_views[uScreenId]->contentsWidth(), iCh = m_views[uScreenId]->contentsHeight();
814 int iVw = m_views[uScreenId]->visibleWidth(), iVh = m_views[uScreenId]->visibleHeight();
815
816 if (vboxGlobal().vmRenderMode() != VBoxDefs::SDLMode)
817 {
818 /* Try to automatically scroll the guest canvas if the
819 * mouse goes outside its visible part: */
820 int iDx = 0;
821 if (relativePos.x() > iVw) iDx = relativePos.x() - iVw;
822 else if (relativePos.x() < 0) iDx = relativePos.x();
823 int iDy = 0;
824 if (relativePos.y() > iVh) iDy = relativePos.y() - iVh;
825 else if (relativePos.y() < 0) iDy = relativePos.y();
826 if (iDx != 0 || iDy != 0) m_views[uScreenId]->scrollBy(iDx, iDy);
827 }
828
829 /* Get mouse-pointer location: */
830 QPoint cpnt = m_views[uScreenId]->viewportToContents(relativePos);
831
832 /* Determine scaling: */
833 UIFrameBuffer *pFrameBuffer = m_views[uScreenId]->frameBuffer();
834 QSize scaledSize = pFrameBuffer->scaledSize();
835 double xRatio = scaledSize.isValid() ? (double)pFrameBuffer->width() / (double)scaledSize.width() : 1;
836 double yRatio = scaledSize.isValid() ? (double)pFrameBuffer->height() / (double)scaledSize.height() : 1;
837 /* Set scaling if scale-factor is present: */
838 cpnt.setX(cpnt.x() * xRatio);
839 cpnt.setY(cpnt.y() * yRatio);
840
841 /* Bound coordinates: */
842 if (cpnt.x() < 0) cpnt.setX(0);
843 else if (cpnt.x() > iCw - 1) cpnt.setX(iCw - 1);
844 if (cpnt.y() < 0) cpnt.setY(0);
845 else if (cpnt.y() > iCh - 1) cpnt.setY(iCh - 1);
846
847 /* Determine shifting: */
848 CFramebuffer framebuffer;
849 LONG xShift = 0, yShift = 0;
850 session().GetConsole().GetDisplay().GetFramebuffer(uScreenId, framebuffer, xShift, yShift);
851 /* Set shifting: */
852 cpnt.setX(cpnt.x() + xShift);
853 cpnt.setY(cpnt.y() + yShift);
854
855 /* Post absolute mouse-event into guest: */
856 CMouse mouse = session().GetConsole().GetMouse();
857 mouse.PutMouseEventAbsolute(cpnt.x() + 1, cpnt.y() + 1, iWheelVertical, iWheelHorizontal, iMouseButtonsState);
858 return true;
859 }
860 else
861 {
862 if (m_views[uScreenId]->hasFocus() && (iEventType == QEvent::MouseButtonRelease && mouseButtons == Qt::NoButton))
863 {
864 if (uisession()->isPaused())
865 {
866 vboxProblem().remindAboutPausedVMInput();
867 }
868 else if (uisession()->isRunning())
869 {
870 /* Temporarily disable auto capture that will take place after this dialog is dismissed because
871 * the capture state is to be defined by the dialog result itself: */
872 uisession()->setAutoCaptureDisabled(true);
873 bool autoConfirmed = false;
874 bool ok = vboxProblem().confirmInputCapture(&autoConfirmed);
875 if (autoConfirmed)
876 uisession()->setAutoCaptureDisabled(false);
877 /* Otherwise, the disable flag will be reset in the next console view's focus in event (since
878 * may happen asynchronously on some platforms, after we return from this code): */
879 if (ok)
880 {
881#ifdef Q_WS_X11
882 /* Make sure that pending FocusOut events from the previous message box are handled,
883 * otherwise the mouse is immediately ungrabbed again: */
884 qApp->processEvents();
885#endif
886 machineLogic()->keyboardHandler()->captureKeyboard(uScreenId);
887 captureMouse(uScreenId);
888 }
889 }
890 }
891 }
892 }
893
894 return false;
895}
896
897#ifdef Q_WS_WIN
898/* This method is actually required only because under win-host
899 * we do not really grab the mouse in case of capturing it: */
900void UIMouseHandler::updateMouseCursorClipping()
901{
902 /* Check if such view && viewport are registered: */
903 if (!m_views.contains(m_iMouseCaptureViewIndex) || !m_viewports.contains(m_iMouseCaptureViewIndex))
904 return;
905
906 if (uisession()->isMouseCaptured())
907 {
908 /* Acquiring visible viewport rectangle: */
909 QRect viewportRectangle = m_viewports[m_iMouseCaptureViewIndex]->visibleRegion().boundingRect();
910 QPoint viewportRectangleGlobalPos = m_views[m_iMouseCaptureViewIndex]->mapToGlobal(m_viewports[m_iMouseCaptureViewIndex]->pos());
911 viewportRectangle.translate(viewportRectangleGlobalPos);
912 viewportRectangle = viewportRectangle.intersected(QApplication::desktop()->availableGeometry());
913 /* Prepare clipping area: */
914 RECT rect = { viewportRectangle.left() + 1, viewportRectangle.top() + 1, viewportRectangle.right(), viewportRectangle.bottom() };
915 ::ClipCursor(&rect);
916 }
917 else
918 {
919 ::ClipCursor(NULL);
920 }
921}
922#endif /* Q_WS_WIN */
923
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use