VirtualBox

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

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

MouseImpl: try to fix XPCOM warning.

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