VirtualBox

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

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

FE/Qt4-OSX: Prevent the mouse from leaving the window in capture mode.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use