VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/MouseImpl.cpp@ 48654

Last change on this file since 48654 was 47848, checked in by vboxsync, 11 years ago

Main: API event for multitouch input.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.4 KB
Line 
1/* $Id: MouseImpl.cpp 47848 2013-08-19 16:34:02Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2012 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#include <iprt/cpp/utils.h>
19
20#include "MouseImpl.h"
21#include "DisplayImpl.h"
22#include "VMMDev.h"
23
24#include "AutoCaller.h"
25#include "Logging.h"
26
27#include <VBox/vmm/pdmdrv.h>
28#include <VBox/VMMDev.h>
29
30#include <iprt/asm.h>
31
32/** @name Mouse device capabilities bitfield
33 * @{ */
34enum
35{
36 /** The mouse device can do relative reporting */
37 MOUSE_DEVCAP_RELATIVE = 1,
38 /** The mouse device can do absolute reporting */
39 MOUSE_DEVCAP_ABSOLUTE = 2,
40 /** The mouse device can do absolute reporting */
41 MOUSE_DEVCAP_MULTI_TOUCH = 4
42};
43/** @} */
44
45
46/**
47 * Mouse driver instance data.
48 */
49struct DRVMAINMOUSE
50{
51 /** Pointer to the mouse object. */
52 Mouse *pMouse;
53 /** Pointer to the driver instance structure. */
54 PPDMDRVINS pDrvIns;
55 /** Pointer to the mouse port interface of the driver/device above us. */
56 PPDMIMOUSEPORT pUpPort;
57 /** Our mouse connector interface. */
58 PDMIMOUSECONNECTOR IConnector;
59 /** The capabilities of this device. */
60 uint32_t u32DevCaps;
61};
62
63
64// constructor / destructor
65/////////////////////////////////////////////////////////////////////////////
66
67Mouse::Mouse()
68 : mParent(NULL)
69{
70}
71
72Mouse::~Mouse()
73{
74}
75
76
77HRESULT Mouse::FinalConstruct()
78{
79 RT_ZERO(mpDrv);
80 mcLastX = 0x8000;
81 mcLastY = 0x8000;
82 mfLastButtons = 0;
83 mfVMMDevGuestCaps = 0;
84 return BaseFinalConstruct();
85}
86
87void Mouse::FinalRelease()
88{
89 uninit();
90 BaseFinalRelease();
91}
92
93// public methods only for internal purposes
94/////////////////////////////////////////////////////////////////////////////
95
96/**
97 * Initializes the mouse object.
98 *
99 * @returns COM result indicator
100 * @param parent handle of our parent object
101 */
102HRESULT Mouse::init (ConsoleMouseInterface *parent)
103{
104 LogFlowThisFunc(("\n"));
105
106 ComAssertRet(parent, E_INVALIDARG);
107
108 /* Enclose the state transition NotReady->InInit->Ready */
109 AutoInitSpan autoInitSpan(this);
110 AssertReturn(autoInitSpan.isOk(), E_FAIL);
111
112 unconst(mParent) = parent;
113
114 unconst(mEventSource).createObject();
115 HRESULT rc = mEventSource->init(static_cast<IMouse*>(this));
116 AssertComRCReturnRC(rc);
117 mMouseEvent.init(mEventSource, VBoxEventType_OnGuestMouse,
118 0, 0, 0, 0, 0, 0);
119
120 /* Confirm a successful initialization */
121 autoInitSpan.setSucceeded();
122
123 return S_OK;
124}
125
126/**
127 * Uninitializes the instance and sets the ready flag to FALSE.
128 * Called either from FinalRelease() or by the parent when it gets destroyed.
129 */
130void Mouse::uninit()
131{
132 LogFlowThisFunc(("\n"));
133
134 /* Enclose the state transition Ready->InUninit->NotReady */
135 AutoUninitSpan autoUninitSpan(this);
136 if (autoUninitSpan.uninitDone())
137 return;
138
139 for (unsigned i = 0; i < MOUSE_MAX_DEVICES; ++i)
140 {
141 if (mpDrv[i])
142 mpDrv[i]->pMouse = NULL;
143 mpDrv[i] = NULL;
144 }
145
146 mMouseEvent.uninit();
147 unconst(mEventSource).setNull();
148 unconst(mParent) = NULL;
149}
150
151
152// IMouse properties
153/////////////////////////////////////////////////////////////////////////////
154
155/** Report the front-end's mouse handling capabilities to the VMM device and
156 * thus to the guest.
157 * @note all calls out of this object are made with no locks held! */
158HRESULT Mouse::updateVMMDevMouseCaps(uint32_t fCapsAdded,
159 uint32_t fCapsRemoved)
160{
161 VMMDevMouseInterface *pVMMDev = mParent->getVMMDevMouseInterface();
162 if (!pVMMDev)
163 return E_FAIL; /* No assertion, as the front-ends can send events
164 * at all sorts of inconvenient times. */
165 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
166 if (!pVMMDevPort)
167 return E_FAIL; /* same here */
168
169 int rc = pVMMDevPort->pfnUpdateMouseCapabilities(pVMMDevPort, fCapsAdded,
170 fCapsRemoved);
171 return RT_SUCCESS(rc) ? S_OK : E_FAIL;
172}
173
174/**
175 * Returns whether the currently active device portfolio can accept absolute
176 * mouse events.
177 *
178 * @returns COM status code
179 * @param absoluteSupported address of result variable
180 */
181STDMETHODIMP Mouse::COMGETTER(AbsoluteSupported) (BOOL *absoluteSupported)
182{
183 if (!absoluteSupported)
184 return E_POINTER;
185
186 AutoCaller autoCaller(this);
187 if (FAILED(autoCaller.rc())) return autoCaller.rc();
188
189 *absoluteSupported = supportsAbs();
190 return S_OK;
191}
192
193/**
194 * Returns whether the currently active device portfolio can accept relative
195 * mouse events.
196 *
197 * @returns COM status code
198 * @param relativeSupported address of result variable
199 */
200STDMETHODIMP Mouse::COMGETTER(RelativeSupported) (BOOL *relativeSupported)
201{
202 if (!relativeSupported)
203 return E_POINTER;
204
205 AutoCaller autoCaller(this);
206 if (FAILED(autoCaller.rc())) return autoCaller.rc();
207
208 *relativeSupported = supportsRel();
209 return S_OK;
210}
211
212/**
213 * Returns whether the currently active device portfolio can accept multi-touch
214 * mouse events.
215 *
216 * @returns COM status code
217 * @param multiTouchSupported address of result variable
218 */
219STDMETHODIMP Mouse::COMGETTER(MultiTouchSupported) (BOOL *multiTouchSupported)
220{
221 if (!multiTouchSupported)
222 return E_POINTER;
223
224 AutoCaller autoCaller(this);
225 if (FAILED(autoCaller.rc())) return autoCaller.rc();
226
227 *multiTouchSupported = supportsMT();
228 return S_OK;
229}
230
231/**
232 * Returns whether the guest can currently switch to drawing the mouse cursor
233 * itself if it is asked to by the front-end.
234 *
235 * @returns COM status code
236 * @param pfNeedsHostCursor address of result variable
237 */
238STDMETHODIMP Mouse::COMGETTER(NeedsHostCursor) (BOOL *pfNeedsHostCursor)
239{
240 if (!pfNeedsHostCursor)
241 return E_POINTER;
242
243 AutoCaller autoCaller(this);
244 if (FAILED(autoCaller.rc())) return autoCaller.rc();
245
246 *pfNeedsHostCursor = guestNeedsHostCursor();
247 return S_OK;
248}
249
250// IMouse methods
251/////////////////////////////////////////////////////////////////////////////
252
253/** Converts a bitfield containing information about mouse buttons currently
254 * held down from the format used by the front-end to the format used by PDM
255 * and the emulated pointing devices. */
256static uint32_t mouseButtonsToPDM(LONG buttonState)
257{
258 uint32_t fButtons = 0;
259 if (buttonState & MouseButtonState_LeftButton)
260 fButtons |= PDMIMOUSEPORT_BUTTON_LEFT;
261 if (buttonState & MouseButtonState_RightButton)
262 fButtons |= PDMIMOUSEPORT_BUTTON_RIGHT;
263 if (buttonState & MouseButtonState_MiddleButton)
264 fButtons |= PDMIMOUSEPORT_BUTTON_MIDDLE;
265 if (buttonState & MouseButtonState_XButton1)
266 fButtons |= PDMIMOUSEPORT_BUTTON_X1;
267 if (buttonState & MouseButtonState_XButton2)
268 fButtons |= PDMIMOUSEPORT_BUTTON_X2;
269 return fButtons;
270}
271
272STDMETHODIMP Mouse::COMGETTER(EventSource)(IEventSource ** aEventSource)
273{
274 CheckComArgOutPointerValid(aEventSource);
275
276 AutoCaller autoCaller(this);
277 if (FAILED(autoCaller.rc())) return autoCaller.rc();
278
279 // no need to lock - lifetime constant
280 mEventSource.queryInterfaceTo(aEventSource);
281
282 return S_OK;
283}
284
285/**
286 * Send a relative pointer event to the relative device we deem most
287 * appropriate.
288 *
289 * @returns COM status code
290 */
291HRESULT Mouse::reportRelEventToMouseDev(int32_t dx, int32_t dy, int32_t dz,
292 int32_t dw, uint32_t fButtons)
293{
294 if (dx || dy || dz || dw || fButtons != mfLastButtons)
295 {
296 PPDMIMOUSEPORT pUpPort = NULL;
297 {
298 AutoReadLock aLock(this COMMA_LOCKVAL_SRC_POS);
299
300 for (unsigned i = 0; !pUpPort && i < MOUSE_MAX_DEVICES; ++i)
301 {
302 if (mpDrv[i] && (mpDrv[i]->u32DevCaps & MOUSE_DEVCAP_RELATIVE))
303 pUpPort = mpDrv[i]->pUpPort;
304 }
305 }
306 if (!pUpPort)
307 return S_OK;
308
309 int vrc = pUpPort->pfnPutEvent(pUpPort, dx, dy, dz, dw, fButtons);
310
311 if (RT_FAILURE(vrc))
312 return setError(VBOX_E_IPRT_ERROR,
313 tr("Could not send the mouse event to the virtual mouse (%Rrc)"),
314 vrc);
315 mfLastButtons = fButtons;
316 }
317 return S_OK;
318}
319
320
321/**
322 * Send an absolute pointer event to the emulated absolute device we deem most
323 * appropriate.
324 *
325 * @returns COM status code
326 */
327HRESULT Mouse::reportAbsEventToMouseDev(int32_t x, int32_t y,
328 int32_t dz, int32_t dw, uint32_t fButtons)
329{
330 if ( x < VMMDEV_MOUSE_RANGE_MIN
331 || x > VMMDEV_MOUSE_RANGE_MAX)
332 return S_OK;
333 if ( y < VMMDEV_MOUSE_RANGE_MIN
334 || y > VMMDEV_MOUSE_RANGE_MAX)
335 return S_OK;
336 if ( x != mcLastX || y != mcLastY
337 || dz || dw || fButtons != mfLastButtons)
338 {
339 PPDMIMOUSEPORT pUpPort = NULL;
340 {
341 AutoReadLock aLock(this COMMA_LOCKVAL_SRC_POS);
342
343 for (unsigned i = 0; !pUpPort && i < MOUSE_MAX_DEVICES; ++i)
344 {
345 if (mpDrv[i] && (mpDrv[i]->u32DevCaps & MOUSE_DEVCAP_ABSOLUTE))
346 pUpPort = mpDrv[i]->pUpPort;
347 }
348 }
349 if (!pUpPort)
350 return S_OK;
351
352 int vrc = pUpPort->pfnPutEventAbs(pUpPort, x, y, dz,
353 dw, fButtons);
354 if (RT_FAILURE(vrc))
355 return setError(VBOX_E_IPRT_ERROR,
356 tr("Could not send the mouse event to the virtual mouse (%Rrc)"),
357 vrc);
358 mfLastButtons = fButtons;
359
360 }
361 return S_OK;
362}
363
364HRESULT Mouse::reportMultiTouchEventToDevice(uint8_t cContacts,
365 const uint64_t *pau64Contacts,
366 uint32_t u32ScanTime)
367{
368 HRESULT hrc = S_OK;
369
370 PPDMIMOUSEPORT pUpPort = NULL;
371 {
372 AutoReadLock aLock(this COMMA_LOCKVAL_SRC_POS);
373
374 unsigned i;
375 for (i = 0; i < MOUSE_MAX_DEVICES; ++i)
376 {
377 if ( mpDrv[i]
378 && (mpDrv[i]->u32DevCaps & MOUSE_DEVCAP_MULTI_TOUCH))
379 {
380 pUpPort = mpDrv[i]->pUpPort;
381 break;
382 }
383 }
384 }
385
386 if (pUpPort)
387 {
388 int vrc = pUpPort->pfnPutEventMultiTouch(pUpPort, cContacts, pau64Contacts, u32ScanTime);
389 if (RT_FAILURE(vrc))
390 hrc = setError(VBOX_E_IPRT_ERROR,
391 tr("Could not send the multi-touch event to the virtual device (%Rrc)"),
392 vrc);
393 }
394 else
395 {
396 hrc = E_UNEXPECTED;
397 }
398
399 return hrc;
400}
401
402
403/**
404 * Send an absolute position event to the VMM device.
405 * @note all calls out of this object are made with no locks held!
406 *
407 * @returns COM status code
408 */
409HRESULT Mouse::reportAbsEventToVMMDev(int32_t x, int32_t y)
410{
411 VMMDevMouseInterface *pVMMDev = mParent->getVMMDevMouseInterface();
412 ComAssertRet(pVMMDev, E_FAIL);
413 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
414 ComAssertRet(pVMMDevPort, E_FAIL);
415
416 if (x != mcLastX || y != mcLastY)
417 {
418 int vrc = pVMMDevPort->pfnSetAbsoluteMouse(pVMMDevPort,
419 x, y);
420 if (RT_FAILURE(vrc))
421 return setError(VBOX_E_IPRT_ERROR,
422 tr("Could not send the mouse event to the virtual mouse (%Rrc)"),
423 vrc);
424 }
425 return S_OK;
426}
427
428
429/**
430 * Send an absolute pointer event to a pointing device (the VMM device if
431 * possible or whatever emulated absolute device seems best to us if not).
432 *
433 * @returns COM status code
434 */
435HRESULT Mouse::reportAbsEvent(int32_t x, int32_t y,
436 int32_t dz, int32_t dw, uint32_t fButtons,
437 bool fUsesVMMDevEvent)
438{
439 HRESULT rc;
440 /** If we are using the VMMDev to report absolute position but without
441 * VMMDev IRQ support then we need to send a small "jiggle" to the emulated
442 * relative mouse device to alert the guest to changes. */
443 LONG cJiggle = 0;
444
445 if (vmmdevCanAbs())
446 {
447 /*
448 * Send the absolute mouse position to the VMM device.
449 */
450 if (x != mcLastX || y != mcLastY)
451 {
452 rc = reportAbsEventToVMMDev(x, y);
453 cJiggle = !fUsesVMMDevEvent;
454 }
455 rc = reportRelEventToMouseDev(cJiggle, 0, dz, dw, fButtons);
456 }
457 else
458 rc = reportAbsEventToMouseDev(x, y, dz, dw, fButtons);
459
460 mcLastX = x;
461 mcLastY = y;
462 return rc;
463}
464
465void Mouse::fireMouseEvent(bool fAbsolute, LONG x, LONG y, LONG dz, LONG dw,
466 LONG fButtons)
467{
468 /* If mouse button is pressed, we generate new event, to avoid reusable events coalescing and thus
469 dropping key press events */
470 GuestMouseEventMode_T mode;
471 if (fAbsolute)
472 mode = GuestMouseEventMode_Absolute;
473 else
474 mode = GuestMouseEventMode_Relative;
475
476 if (fButtons != 0)
477 {
478 VBoxEventDesc evDesc;
479 evDesc.init(mEventSource, VBoxEventType_OnGuestMouse, mode, x, y,
480 dz, dw, fButtons);
481 evDesc.fire(0);
482 }
483 else
484 {
485 mMouseEvent.reinit(VBoxEventType_OnGuestMouse, mode, x, y, dz, dw,
486 fButtons);
487 mMouseEvent.fire(0);
488 }
489}
490
491void Mouse::fireMultiTouchEvent(uint8_t cContacts,
492 const LONG64 *paContacts,
493 uint32_t u32ScanTime)
494{
495 com::SafeArray<SHORT> xPositions(cContacts);
496 com::SafeArray<SHORT> yPositions(cContacts);
497 com::SafeArray<USHORT> contactIds(cContacts);
498 com::SafeArray<USHORT> contactFlags(cContacts);
499
500 uint8_t i;
501 for (i = 0; i < cContacts; i++)
502 {
503 uint32_t u32Lo = RT_LO_U32(paContacts[i]);
504 uint32_t u32Hi = RT_HI_U32(paContacts[i]);
505 xPositions[i] = (int16_t)u32Lo;
506 yPositions[i] = (int16_t)(u32Lo >> 16);
507 contactIds[i] = RT_BYTE1(u32Hi);
508 contactFlags[i] = RT_BYTE2(u32Hi);
509 }
510
511 VBoxEventDesc evDesc;
512 evDesc.init(mEventSource, VBoxEventType_OnGuestMultiTouch,
513 cContacts, ComSafeArrayAsInParam(xPositions), ComSafeArrayAsInParam(yPositions),
514 ComSafeArrayAsInParam(contactIds), ComSafeArrayAsInParam(contactFlags), u32ScanTime);
515 evDesc.fire(0);
516}
517
518/**
519 * Send a relative mouse event to the guest.
520 * @note the VMMDev capability change is so that the guest knows we are sending
521 * real events over the PS/2 device and not dummy events to signal the
522 * arrival of new absolute pointer data
523 *
524 * @returns COM status code
525 * @param dx X movement
526 * @param dy Y movement
527 * @param dz Z movement
528 * @param fButtons The mouse button state
529 */
530STDMETHODIMP Mouse::PutMouseEvent(LONG dx, LONG dy, LONG dz, LONG dw,
531 LONG fButtons)
532{
533 HRESULT rc;
534 uint32_t fButtonsAdj;
535
536 AutoCaller autoCaller(this);
537 if (FAILED(autoCaller.rc())) return autoCaller.rc();
538 LogRel3(("%s: dx=%d, dy=%d, dz=%d, dw=%d\n", __PRETTY_FUNCTION__,
539 dx, dy, dz, dw));
540
541 fButtonsAdj = mouseButtonsToPDM(fButtons);
542 /* Make sure that the guest knows that we are sending real movement
543 * events to the PS/2 device and not just dummy wake-up ones. */
544 updateVMMDevMouseCaps(0, VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE);
545 rc = reportRelEventToMouseDev(dx, dy, dz, dw, fButtonsAdj);
546
547 fireMouseEvent(false, dx, dy, dz, dw, fButtons);
548
549 return rc;
550}
551
552/**
553 * Convert an (X, Y) value pair in screen co-ordinates (starting from 1) to a
554 * value from VMMDEV_MOUSE_RANGE_MIN to VMMDEV_MOUSE_RANGE_MAX. Sets the
555 * optional validity value to false if the pair is not on an active screen and
556 * to true otherwise.
557 * @note since guests with recent versions of X.Org use a different method
558 * to everyone else to map the valuator value to a screen pixel (they
559 * multiply by the screen dimension, do a floating point divide by
560 * the valuator maximum and round the result, while everyone else
561 * does truncating integer operations) we adjust the value we send
562 * so that it maps to the right pixel both when the result is rounded
563 * and when it is truncated.
564 *
565 * @returns COM status value
566 */
567HRESULT Mouse::convertDisplayRes(LONG x, LONG y, int32_t *pxAdj, int32_t *pyAdj,
568 bool *pfValid)
569{
570 AssertPtrReturn(pxAdj, E_POINTER);
571 AssertPtrReturn(pyAdj, E_POINTER);
572 AssertPtrNullReturn(pfValid, E_POINTER);
573 DisplayMouseInterface *pDisplay = mParent->getDisplayMouseInterface();
574 ComAssertRet(pDisplay, E_FAIL);
575 /** The amount to add to the result (multiplied by the screen width/height)
576 * to compensate for differences in guest methods for mapping back to
577 * pixels */
578 enum { ADJUST_RANGE = - 3 * VMMDEV_MOUSE_RANGE / 4 };
579
580 if (pfValid)
581 *pfValid = true;
582 if (!(mfVMMDevGuestCaps & VMMDEV_MOUSE_NEW_PROTOCOL))
583 {
584 ULONG displayWidth, displayHeight;
585 /* Takes the display lock */
586 HRESULT rc = pDisplay->getScreenResolution(0, &displayWidth,
587 &displayHeight, NULL, NULL, NULL);
588 if (FAILED(rc))
589 return rc;
590
591 *pxAdj = displayWidth ? (x * VMMDEV_MOUSE_RANGE + ADJUST_RANGE)
592 / (LONG) displayWidth: 0;
593 *pyAdj = displayHeight ? (y * VMMDEV_MOUSE_RANGE + ADJUST_RANGE)
594 / (LONG) displayHeight: 0;
595 }
596 else
597 {
598 int32_t x1, y1, x2, y2;
599 /* Takes the display lock */
600 pDisplay->getFramebufferDimensions(&x1, &y1, &x2, &y2);
601 *pxAdj = x1 < x2 ? ((x - x1) * VMMDEV_MOUSE_RANGE + ADJUST_RANGE)
602 / (x2 - x1) : 0;
603 *pyAdj = y1 < y2 ? ((y - y1) * VMMDEV_MOUSE_RANGE + ADJUST_RANGE)
604 / (y2 - y1) : 0;
605 if ( *pxAdj < VMMDEV_MOUSE_RANGE_MIN
606 || *pxAdj > VMMDEV_MOUSE_RANGE_MAX
607 || *pyAdj < VMMDEV_MOUSE_RANGE_MIN
608 || *pyAdj > VMMDEV_MOUSE_RANGE_MAX)
609 if (pfValid)
610 *pfValid = false;
611 }
612 return S_OK;
613}
614
615
616/**
617 * Send an absolute mouse event to the VM. This requires either VirtualBox-
618 * specific drivers installed in the guest or absolute pointing device
619 * emulation.
620 * @note the VMMDev capability change is so that the guest knows we are sending
621 * dummy events over the PS/2 device to signal the arrival of new
622 * absolute pointer data, and not pointer real movement data
623 * @note all calls out of this object are made with no locks held!
624 *
625 * @returns COM status code
626 * @param x X position (pixel), starting from 1
627 * @param y Y position (pixel), starting from 1
628 * @param dz Z movement
629 * @param fButtons The mouse button state
630 */
631STDMETHODIMP Mouse::PutMouseEventAbsolute(LONG x, LONG y, LONG dz, LONG dw,
632 LONG fButtons)
633{
634 AutoCaller autoCaller(this);
635 if (FAILED(autoCaller.rc())) return autoCaller.rc();
636
637 LogRel3(("%s: x=%d, y=%d, dz=%d, dw=%d, fButtons=0x%x\n",
638 __PRETTY_FUNCTION__, x, y, dz, dw, fButtons));
639
640 int32_t xAdj, yAdj;
641 uint32_t fButtonsAdj;
642 bool fValid;
643
644 /** @todo the front end should do this conversion to avoid races */
645 /** @note Or maybe not... races are pretty inherent in everything done in
646 * this object and not really bad as far as I can see. */
647 HRESULT rc = convertDisplayRes(x, y, &xAdj, &yAdj, &fValid);
648 if (FAILED(rc)) return rc;
649
650 fButtonsAdj = mouseButtonsToPDM(fButtons);
651 /* If we are doing old-style (IRQ-less) absolute reporting to the VMM
652 * device then make sure the guest is aware of it, so that it knows to
653 * ignore relative movement on the PS/2 device. */
654 updateVMMDevMouseCaps(VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE, 0);
655 if (fValid)
656 {
657 rc = reportAbsEvent(xAdj, yAdj, dz, dw, fButtonsAdj,
658 RT_BOOL( mfVMMDevGuestCaps
659 & VMMDEV_MOUSE_NEW_PROTOCOL));
660
661 fireMouseEvent(true, x, y, dz, dw, fButtons);
662 }
663
664 return rc;
665}
666
667/**
668 * Send a multi-touch event. This requires multi-touch pointing device emulation.
669 * @note all calls out of this object are made with no locks held!
670 *
671 * @returns COM status code.
672 * @param aCount Number of contacts.
673 * @param aContacts Information about each contact.
674 * @param aScanTime Timestamp.
675 */
676STDMETHODIMP Mouse::PutEventMultiTouch(LONG aCount,
677 ComSafeArrayIn(LONG64, aContacts),
678 ULONG aScanTime)
679{
680 AutoCaller autoCaller(this);
681 if (FAILED(autoCaller.rc())) return autoCaller.rc();
682
683 com::SafeArray <LONG64> arrayContacts(ComSafeArrayInArg(aContacts));
684
685 LogRel3(("%s: aCount %d(actual %d), aScanTime %u\n",
686 __FUNCTION__, aCount, arrayContacts.size(), aScanTime));
687
688 HRESULT rc = S_OK;
689
690 if ((LONG)arrayContacts.size() >= aCount)
691 {
692 LONG64* paContacts = arrayContacts.raw();
693
694 rc = putEventMultiTouch(aCount, paContacts, aScanTime);
695 }
696 else
697 {
698 rc = E_INVALIDARG;
699 }
700
701 return rc;
702}
703
704/**
705 * Send a multi-touch event. Version for scripting languages.
706 *
707 * @returns COM status code.
708 * @param aCount Number of contacts.
709 * @param aContacts Information about each contact.
710 * @param aScanTime Timestamp.
711 */
712STDMETHODIMP Mouse::PutEventMultiTouchString(LONG aCount,
713 IN_BSTR aContacts,
714 ULONG aScanTime)
715{
716 /** @todo implement: convert the string to LONG64 array and call putEventMultiTouch. */
717 NOREF(aCount);
718 NOREF(aContacts);
719 NOREF(aScanTime);
720 return E_NOTIMPL;
721}
722
723
724// private methods
725/////////////////////////////////////////////////////////////////////////////
726
727/* Used by PutEventMultiTouch and PutEventMultiTouchString. */
728HRESULT Mouse::putEventMultiTouch(LONG aCount,
729 LONG64 *paContacts,
730 ULONG aScanTime)
731{
732 if (aCount >= 256)
733 {
734 return E_INVALIDARG;
735 }
736
737 DisplayMouseInterface *pDisplay = mParent->getDisplayMouseInterface();
738 ComAssertRet(pDisplay, E_FAIL);
739
740 /* Touch events are mapped to the primary monitor, because the emulated USB
741 * touchscreen device is associated with one (normally the primary) screen in the guest.
742 */
743 ULONG uScreenId = 0;
744
745 ULONG cWidth = 0;
746 ULONG cHeight = 0;
747 LONG xOrigin = 0;
748 LONG yOrigin = 0;
749 HRESULT rc = pDisplay->getScreenResolution(uScreenId, &cWidth, &cHeight, NULL, &xOrigin, &yOrigin);
750 ComAssertComRCRetRC(rc);
751
752 uint64_t* pau64Contacts = NULL;
753 uint8_t cContacts = 0;
754
755 /* Deliver 0 contacts too, touch device may use this to reset the state. */
756 if (aCount > 0)
757 {
758 /* Create a copy with converted coords. */
759 pau64Contacts = (uint64_t *)RTMemTmpAlloc(aCount * sizeof(uint64_t));
760 if (pau64Contacts)
761 {
762 int32_t x1 = xOrigin;
763 int32_t y1 = yOrigin;
764 int32_t x2 = x1 + cWidth;
765 int32_t y2 = y1 + cHeight;
766
767 LogRel3(("%s: screen [%d] %d,%d %d,%d\n",
768 __FUNCTION__, uScreenId, x1, y1, x2, y2));
769
770 LONG i;
771 for (i = 0; i < aCount; i++)
772 {
773 uint32_t u32Lo = RT_LO_U32(paContacts[i]);
774 uint32_t u32Hi = RT_HI_U32(paContacts[i]);
775 int32_t x = (int16_t)u32Lo;
776 int32_t y = (int16_t)(u32Lo >> 16);
777 uint8_t contactId = RT_BYTE1(u32Hi);
778 bool fInContact = (RT_BYTE2(u32Hi) & 0x1) != 0;
779 bool fInRange = (RT_BYTE2(u32Hi) & 0x2) != 0;
780
781 LogRel3(("%s: [%d] %d,%d id %d, inContact %d, inRange %d\n",
782 __FUNCTION__, i, x, y, contactId, fInContact, fInRange));
783
784 /* x1,y1 are inclusive and x2,y2 are exclusive,
785 * while x,y start from 1 and are inclusive.
786 */
787 if (x <= x1 || x > x2 || y <= y1 || y > y2)
788 {
789 /* Out of range. Skip the contact. */
790 continue;
791 }
792
793 int32_t xAdj = x1 < x2? ((x - 1 - x1) * VMMDEV_MOUSE_RANGE) / (x2 - x1) : 0;
794 int32_t yAdj = y1 < y2? ((y - 1 - y1) * VMMDEV_MOUSE_RANGE) / (y2 - y1) : 0;
795
796 bool fValid = ( xAdj >= VMMDEV_MOUSE_RANGE_MIN
797 && xAdj <= VMMDEV_MOUSE_RANGE_MAX
798 && yAdj >= VMMDEV_MOUSE_RANGE_MIN
799 && yAdj <= VMMDEV_MOUSE_RANGE_MAX);
800
801 if (fValid)
802 {
803 uint8_t fu8 = (fInContact? 0x01: 0x00)
804 | (fInRange? 0x02: 0x00);
805 pau64Contacts[cContacts] = RT_MAKE_U64_FROM_U16((uint16_t)xAdj,
806 (uint16_t)yAdj,
807 RT_MAKE_U16(contactId, fu8),
808 0);
809 cContacts++;
810 }
811 }
812 }
813 else
814 {
815 rc = E_OUTOFMEMORY;
816 }
817 }
818
819 if (SUCCEEDED(rc))
820 {
821 rc = reportMultiTouchEventToDevice(cContacts, cContacts? pau64Contacts: NULL, (uint32_t)aScanTime);
822
823 /* Send the original contact information. */
824 fireMultiTouchEvent(cContacts, cContacts? paContacts: NULL, (uint32_t)aScanTime);
825 }
826
827 RTMemTmpFree(pau64Contacts);
828
829 return rc;
830}
831
832
833/** Does the guest currently rely on the host to draw the mouse cursor or
834 * can it switch to doing it itself in software? */
835bool Mouse::guestNeedsHostCursor(void)
836{
837 return RT_BOOL(mfVMMDevGuestCaps & VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR);
838}
839
840
841/** Check what sort of reporting can be done using the devices currently
842 * enabled. Does not consider the VMM device. */
843void Mouse::getDeviceCaps(bool *pfAbs, bool *pfRel, bool *pfMT)
844{
845 bool fAbsDev = false;
846 bool fRelDev = false;
847 bool fMTDev = false;
848
849 AutoReadLock aLock(this COMMA_LOCKVAL_SRC_POS);
850
851 for (unsigned i = 0; i < MOUSE_MAX_DEVICES; ++i)
852 if (mpDrv[i])
853 {
854 if (mpDrv[i]->u32DevCaps & MOUSE_DEVCAP_ABSOLUTE)
855 fAbsDev = true;
856 if (mpDrv[i]->u32DevCaps & MOUSE_DEVCAP_RELATIVE)
857 fRelDev = true;
858 if (mpDrv[i]->u32DevCaps & MOUSE_DEVCAP_MULTI_TOUCH)
859 fMTDev = true;
860 }
861 if (pfAbs)
862 *pfAbs = fAbsDev;
863 if (pfRel)
864 *pfRel = fRelDev;
865 if (pfMT)
866 *pfMT = fMTDev;
867}
868
869
870/** Does the VMM device currently support absolute reporting? */
871bool Mouse::vmmdevCanAbs(void)
872{
873 bool fRelDev;
874
875 getDeviceCaps(NULL, &fRelDev, NULL);
876 return (mfVMMDevGuestCaps & VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE)
877 && fRelDev;
878}
879
880
881/** Does the VMM device currently support absolute reporting? */
882bool Mouse::deviceCanAbs(void)
883{
884 bool fAbsDev;
885
886 getDeviceCaps(&fAbsDev, NULL, NULL);
887 return fAbsDev;
888}
889
890
891/** Can we currently send relative events to the guest? */
892bool Mouse::supportsRel(void)
893{
894 bool fRelDev;
895
896 getDeviceCaps(NULL, &fRelDev, NULL);
897 return fRelDev;
898}
899
900
901/** Can we currently send absolute events to the guest? */
902bool Mouse::supportsAbs(void)
903{
904 bool fAbsDev;
905
906 getDeviceCaps(&fAbsDev, NULL, NULL);
907 return fAbsDev || vmmdevCanAbs();
908}
909
910
911/** Can we currently send absolute events to the guest? */
912bool Mouse::supportsMT(void)
913{
914 bool fMTDev;
915
916 getDeviceCaps(NULL, NULL, &fMTDev);
917 return fMTDev;
918}
919
920
921/** Check what sort of reporting can be done using the devices currently
922 * enabled (including the VMM device) and notify the guest and the front-end.
923 */
924void Mouse::sendMouseCapsNotifications(void)
925{
926 bool fAbsDev, fRelDev, fMTDev, fCanAbs, fNeedsHostCursor;
927
928 {
929 AutoReadLock aLock(this COMMA_LOCKVAL_SRC_POS);
930
931 getDeviceCaps(&fAbsDev, &fRelDev, &fMTDev);
932 fCanAbs = supportsAbs();
933 fNeedsHostCursor = guestNeedsHostCursor();
934 }
935 if (fAbsDev)
936 updateVMMDevMouseCaps(VMMDEV_MOUSE_HOST_HAS_ABS_DEV, 0);
937 else
938 updateVMMDevMouseCaps(0, VMMDEV_MOUSE_HOST_HAS_ABS_DEV);
939 /** @todo this call takes the Console lock in order to update the cached
940 * callback data atomically. However I can't see any sign that the cached
941 * data is ever used again. */
942 mParent->onMouseCapabilityChange(fCanAbs, fRelDev, fMTDev, fNeedsHostCursor);
943}
944
945
946/**
947 * @interface_method_impl{PDMIMOUSECONNECTOR,pfnReportModes}
948 * A virtual device is notifying us about its current state and capabilities
949 */
950DECLCALLBACK(void) Mouse::mouseReportModes(PPDMIMOUSECONNECTOR pInterface, bool fRel, bool fAbs, bool fMT)
951{
952 PDRVMAINMOUSE pDrv = RT_FROM_MEMBER(pInterface, DRVMAINMOUSE, IConnector);
953 if (fRel)
954 pDrv->u32DevCaps |= MOUSE_DEVCAP_RELATIVE;
955 else
956 pDrv->u32DevCaps &= ~MOUSE_DEVCAP_RELATIVE;
957 if (fAbs)
958 pDrv->u32DevCaps |= MOUSE_DEVCAP_ABSOLUTE;
959 else
960 pDrv->u32DevCaps &= ~MOUSE_DEVCAP_ABSOLUTE;
961 if (fMT)
962 pDrv->u32DevCaps |= MOUSE_DEVCAP_MULTI_TOUCH;
963 else
964 pDrv->u32DevCaps &= ~MOUSE_DEVCAP_MULTI_TOUCH;
965
966 pDrv->pMouse->sendMouseCapsNotifications();
967}
968
969
970/**
971 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
972 */
973DECLCALLBACK(void *) Mouse::drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
974{
975 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
976 PDRVMAINMOUSE pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINMOUSE);
977
978 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
979 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUSECONNECTOR, &pDrv->IConnector);
980 return NULL;
981}
982
983
984/**
985 * Destruct a mouse driver instance.
986 *
987 * @returns VBox status.
988 * @param pDrvIns The driver instance data.
989 */
990DECLCALLBACK(void) Mouse::drvDestruct(PPDMDRVINS pDrvIns)
991{
992 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
993 PDRVMAINMOUSE pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINMOUSE);
994 LogFlow(("Mouse::drvDestruct: iInstance=%d\n", pDrvIns->iInstance));
995
996 if (pThis->pMouse)
997 {
998 AutoWriteLock mouseLock(pThis->pMouse COMMA_LOCKVAL_SRC_POS);
999 for (unsigned cDev = 0; cDev < MOUSE_MAX_DEVICES; ++cDev)
1000 if (pThis->pMouse->mpDrv[cDev] == pThis)
1001 {
1002 pThis->pMouse->mpDrv[cDev] = NULL;
1003 break;
1004 }
1005 }
1006}
1007
1008
1009/**
1010 * Construct a mouse driver instance.
1011 *
1012 * @copydoc FNPDMDRVCONSTRUCT
1013 */
1014DECLCALLBACK(int) Mouse::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1015{
1016 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1017 PDRVMAINMOUSE pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINMOUSE);
1018 LogFlow(("drvMainMouse_Construct: iInstance=%d\n", pDrvIns->iInstance));
1019
1020 /*
1021 * Validate configuration.
1022 */
1023 if (!CFGMR3AreValuesValid(pCfg, "Object\0"))
1024 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
1025 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
1026 ("Configuration error: Not possible to attach anything to this driver!\n"),
1027 VERR_PDM_DRVINS_NO_ATTACH);
1028
1029 /*
1030 * IBase.
1031 */
1032 pDrvIns->IBase.pfnQueryInterface = Mouse::drvQueryInterface;
1033
1034 pThis->IConnector.pfnReportModes = Mouse::mouseReportModes;
1035
1036 /*
1037 * Get the IMousePort interface of the above driver/device.
1038 */
1039 pThis->pUpPort = (PPDMIMOUSEPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMIMOUSEPORT_IID);
1040 if (!pThis->pUpPort)
1041 {
1042 AssertMsgFailed(("Configuration error: No mouse port interface above!\n"));
1043 return VERR_PDM_MISSING_INTERFACE_ABOVE;
1044 }
1045
1046 /*
1047 * Get the Mouse object pointer and update the mpDrv member.
1048 */
1049 void *pv;
1050 int rc = CFGMR3QueryPtr(pCfg, "Object", &pv);
1051 if (RT_FAILURE(rc))
1052 {
1053 AssertMsgFailed(("Configuration error: No/bad \"Object\" value! rc=%Rrc\n", rc));
1054 return rc;
1055 }
1056 pThis->pMouse = (Mouse *)pv; /** @todo Check this cast! */
1057 unsigned cDev;
1058 {
1059 AutoReadLock mouseLock(pThis->pMouse COMMA_LOCKVAL_SRC_POS);
1060
1061 for (cDev = 0; cDev < MOUSE_MAX_DEVICES; ++cDev)
1062 if (!pThis->pMouse->mpDrv[cDev])
1063 {
1064 pThis->pMouse->mpDrv[cDev] = pThis;
1065 break;
1066 }
1067 }
1068 if (cDev == MOUSE_MAX_DEVICES)
1069 return VERR_NO_MORE_HANDLES;
1070
1071 return VINF_SUCCESS;
1072}
1073
1074
1075/**
1076 * Main mouse driver registration record.
1077 */
1078const PDMDRVREG Mouse::DrvReg =
1079{
1080 /* u32Version */
1081 PDM_DRVREG_VERSION,
1082 /* szName */
1083 "MainMouse",
1084 /* szRCMod */
1085 "",
1086 /* szR0Mod */
1087 "",
1088 /* pszDescription */
1089 "Main mouse driver (Main as in the API).",
1090 /* fFlags */
1091 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1092 /* fClass. */
1093 PDM_DRVREG_CLASS_MOUSE,
1094 /* cMaxInstances */
1095 ~0U,
1096 /* cbInstance */
1097 sizeof(DRVMAINMOUSE),
1098 /* pfnConstruct */
1099 Mouse::drvConstruct,
1100 /* pfnDestruct */
1101 Mouse::drvDestruct,
1102 /* pfnRelocate */
1103 NULL,
1104 /* pfnIOCtl */
1105 NULL,
1106 /* pfnPowerOn */
1107 NULL,
1108 /* pfnReset */
1109 NULL,
1110 /* pfnSuspend */
1111 NULL,
1112 /* pfnResume */
1113 NULL,
1114 /* pfnAttach */
1115 NULL,
1116 /* pfnDetach */
1117 NULL,
1118 /* pfnPowerOff */
1119 NULL,
1120 /* pfnSoftReset */
1121 NULL,
1122 /* u32EndVersion */
1123 PDM_DRVREG_VERSION
1124};
1125/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette