VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/KeyboardImpl.cpp@ 49498

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

Main: Added a releaseKeys method to IKeyboard.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.5 KB
Line 
1/* $Id: KeyboardImpl.cpp 49386 2013-11-04 16:39:46Z 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 "KeyboardImpl.h"
19#include "ConsoleImpl.h"
20
21#include "AutoCaller.h"
22#include "Logging.h"
23
24#include <VBox/com/array.h>
25#include <VBox/vmm/pdmdrv.h>
26
27#include <iprt/asm.h>
28#include <iprt/cpp/utils.h>
29
30// defines
31////////////////////////////////////////////////////////////////////////////////
32
33// globals
34////////////////////////////////////////////////////////////////////////////////
35
36/** @name Keyboard device capabilities bitfield
37 * @{ */
38enum
39{
40 /** The keyboard device does not wish to receive keystrokes. */
41 KEYBOARD_DEVCAP_DISABLED = 0,
42 /** The keyboard device does wishes to receive keystrokes. */
43 KEYBOARD_DEVCAP_ENABLED = 1
44};
45
46/**
47 * Keyboard driver instance data.
48 */
49typedef struct DRVMAINKEYBOARD
50{
51 /** Pointer to the keyboard object. */
52 Keyboard *pKeyboard;
53 /** Pointer to the driver instance structure. */
54 PPDMDRVINS pDrvIns;
55 /** Pointer to the keyboard port interface of the driver/device above us. */
56 PPDMIKEYBOARDPORT pUpPort;
57 /** Our keyboard connector interface. */
58 PDMIKEYBOARDCONNECTOR IConnector;
59 /** The capabilities of this device. */
60 uint32_t u32DevCaps;
61} DRVMAINKEYBOARD, *PDRVMAINKEYBOARD;
62
63/** Converts PDMIVMMDEVCONNECTOR pointer to a DRVMAINVMMDEV pointer. */
64#define PPDMIKEYBOARDCONNECTOR_2_MAINKEYBOARD(pInterface) ( (PDRVMAINKEYBOARD) ((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINKEYBOARD, IConnector)) )
65
66
67// constructor / destructor
68////////////////////////////////////////////////////////////////////////////////
69
70Keyboard::Keyboard()
71 : mParent(NULL)
72{
73}
74
75Keyboard::~Keyboard()
76{
77}
78
79HRESULT Keyboard::FinalConstruct()
80{
81 RT_ZERO(mpDrv);
82 mpVMMDev = NULL;
83 mfVMMDevInited = false;
84 return BaseFinalConstruct();
85}
86
87void Keyboard::FinalRelease()
88{
89 uninit();
90 BaseFinalRelease();
91}
92
93// public methods
94////////////////////////////////////////////////////////////////////////////////
95
96/**
97 * Initializes the keyboard object.
98 *
99 * @returns COM result indicator
100 * @param parent handle of our parent object
101 */
102HRESULT Keyboard::init(Console *aParent)
103{
104 LogFlowThisFunc(("aParent=%p\n", aParent));
105
106 ComAssertRet(aParent, 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) = aParent;
113
114 unconst(mEventSource).createObject();
115 HRESULT rc = mEventSource->init(static_cast<IKeyboard*>(this));
116 AssertComRCReturnRC(rc);
117
118 /* Confirm a successful initialization */
119 autoInitSpan.setSucceeded();
120
121 return S_OK;
122}
123
124/**
125 * Uninitializes the instance and sets the ready flag to FALSE.
126 * Called either from FinalRelease() or by the parent when it gets destroyed.
127 */
128void Keyboard::uninit()
129{
130 LogFlowThisFunc(("\n"));
131
132 /* Enclose the state transition Ready->InUninit->NotReady */
133 AutoUninitSpan autoUninitSpan(this);
134 if (autoUninitSpan.uninitDone())
135 return;
136
137 for (unsigned i = 0; i < KEYBOARD_MAX_DEVICES; ++i)
138 {
139 if (mpDrv[i])
140 mpDrv[i]->pKeyboard = NULL;
141 mpDrv[i] = NULL;
142 }
143
144 mpVMMDev = NULL;
145 mfVMMDevInited = true;
146
147 unconst(mParent) = NULL;
148 unconst(mEventSource).setNull();
149}
150
151/**
152 * Sends a scancode to the keyboard.
153 *
154 * @returns COM status code
155 * @param scancode The scancode to send
156 */
157STDMETHODIMP Keyboard::PutScancode(LONG scancode)
158{
159 com::SafeArray<LONG> scancodes(1);
160 scancodes[0] = scancode;
161 return PutScancodes(ComSafeArrayAsInParam(scancodes), NULL);
162}
163
164/**
165 * Sends a list of scancodes to the keyboard.
166 *
167 * @returns COM status code
168 * @param scancodes Pointer to the first scancode
169 * @param count Number of scancodes
170 * @param codesStored Address of variable to store the number
171 * of scancodes that were sent to the keyboard.
172 This value can be NULL.
173 */
174STDMETHODIMP Keyboard::PutScancodes(ComSafeArrayIn(LONG, scancodes),
175 ULONG *codesStored)
176{
177 if (ComSafeArrayInIsNull(scancodes))
178 return E_INVALIDARG;
179
180 AutoCaller autoCaller(this);
181 if (FAILED(autoCaller.rc())) return autoCaller.rc();
182
183 com::SafeArray<LONG> keys(ComSafeArrayInArg(scancodes));
184
185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
186
187 CHECK_CONSOLE_DRV(mpDrv[0]);
188
189 /* Send input to the last enabled device. Relies on the fact that
190 * the USB keyboard is always initialized after the PS/2 keyboard.
191 */
192 PPDMIKEYBOARDPORT pUpPort = NULL;
193 for (int i = KEYBOARD_MAX_DEVICES - 1; i >= 0 ; --i)
194 {
195 if (mpDrv[i] && (mpDrv[i]->u32DevCaps & KEYBOARD_DEVCAP_ENABLED))
196 {
197 pUpPort = mpDrv[i]->pUpPort;
198 break;
199 }
200 }
201 /* No enabled keyboard - throw the input away. */
202 if (!pUpPort)
203 {
204 if (codesStored)
205 *codesStored = (uint32_t)keys.size();
206 return S_OK;
207 }
208
209 int vrc = VINF_SUCCESS;
210
211 uint32_t sent;
212 for (sent = 0; (sent < keys.size()) && RT_SUCCESS(vrc); sent++)
213 vrc = pUpPort->pfnPutEvent(pUpPort, (uint8_t)keys[sent]);
214
215 if (codesStored)
216 *codesStored = sent;
217
218 /* Only signal the keys in the event which have been actually sent. */
219 com::SafeArray<LONG> keysSent(sent);
220 memcpy(keysSent.raw(), keys.raw(), sent*sizeof(LONG));
221
222 VBoxEventDesc evDesc;
223 evDesc.init(mEventSource, VBoxEventType_OnGuestKeyboard, ComSafeArrayAsInParam(keys));
224 evDesc.fire(0);
225
226 if (RT_FAILURE(vrc))
227 return setError(VBOX_E_IPRT_ERROR,
228 tr("Could not send all scan codes to the virtual keyboard (%Rrc)"),
229 vrc);
230
231 return S_OK;
232}
233
234/**
235 * Sends Control-Alt-Delete to the keyboard. This could be done otherwise
236 * but it's so common that we'll be nice and supply a convenience API.
237 *
238 * @returns COM status code
239 *
240 */
241STDMETHODIMP Keyboard::PutCAD()
242{
243 static com::SafeArray<LONG> cadSequence(8);
244
245 cadSequence[0] = 0x1d; // Ctrl down
246 cadSequence[1] = 0x38; // Alt down
247 cadSequence[2] = 0xe0; // Del down 1
248 cadSequence[3] = 0x53; // Del down 2
249 cadSequence[4] = 0xe0; // Del up 1
250 cadSequence[5] = 0xd3; // Del up 2
251 cadSequence[6] = 0xb8; // Alt up
252 cadSequence[7] = 0x9d; // Ctrl up
253
254 return PutScancodes(ComSafeArrayAsInParam(cadSequence), NULL);
255}
256
257/**
258 * Releases all currently held keys in the virtual keyboard.
259 *
260 * @returns COM status code
261 *
262 */
263STDMETHODIMP Keyboard::ReleaseKeys()
264{
265 com::SafeArray<LONG> scancodes(1);
266 scancodes[0] = 0xFC; /* Magic scancode, see PS/2 and USB keyboard devices. */
267 return PutScancodes(ComSafeArrayAsInParam(scancodes), NULL);
268}
269
270STDMETHODIMP Keyboard::COMGETTER(EventSource)(IEventSource ** aEventSource)
271{
272 CheckComArgOutPointerValid(aEventSource);
273
274 AutoCaller autoCaller(this);
275 if (FAILED(autoCaller.rc())) return autoCaller.rc();
276
277 // no need to lock - lifetime constant
278 mEventSource.queryInterfaceTo(aEventSource);
279
280 return S_OK;
281}
282
283//
284// private methods
285//
286
287DECLCALLBACK(void) Keyboard::keyboardLedStatusChange(PPDMIKEYBOARDCONNECTOR pInterface, PDMKEYBLEDS enmLeds)
288{
289 PDRVMAINKEYBOARD pDrv = PPDMIKEYBOARDCONNECTOR_2_MAINKEYBOARD(pInterface);
290 pDrv->pKeyboard->getParent()->onKeyboardLedsChange(RT_BOOL(enmLeds & PDMKEYBLEDS_NUMLOCK),
291 RT_BOOL(enmLeds & PDMKEYBLEDS_CAPSLOCK),
292 RT_BOOL(enmLeds & PDMKEYBLEDS_SCROLLLOCK));
293}
294
295/**
296 * @interface_method_impl{PDMIKEYBOARDCONNECTOR,pfnSetActive}
297 */
298DECLCALLBACK(void) Keyboard::keyboardSetActive(PPDMIKEYBOARDCONNECTOR pInterface, bool fActive)
299{
300 PDRVMAINKEYBOARD pDrv = PPDMIKEYBOARDCONNECTOR_2_MAINKEYBOARD(pInterface);
301 if (fActive)
302 pDrv->u32DevCaps |= KEYBOARD_DEVCAP_ENABLED;
303 else
304 pDrv->u32DevCaps &= ~KEYBOARD_DEVCAP_ENABLED;
305}
306
307
308/**
309 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
310 */
311DECLCALLBACK(void *) Keyboard::drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
312{
313 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
314 PDRVMAINKEYBOARD pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
315
316 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
317 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIKEYBOARDCONNECTOR, &pDrv->IConnector);
318 return NULL;
319}
320
321
322/**
323 * Destruct a keyboard driver instance.
324 *
325 * @returns VBox status.
326 * @param pDrvIns The driver instance data.
327 */
328DECLCALLBACK(void) Keyboard::drvDestruct(PPDMDRVINS pDrvIns)
329{
330 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
331 PDRVMAINKEYBOARD pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
332 LogFlow(("Keyboard::drvDestruct: iInstance=%d\n", pDrvIns->iInstance));
333
334 if (pThis->pKeyboard)
335 {
336 AutoWriteLock kbdLock(pThis->pKeyboard COMMA_LOCKVAL_SRC_POS);
337 for (unsigned cDev = 0; cDev < KEYBOARD_MAX_DEVICES; ++cDev)
338 if (pThis->pKeyboard->mpDrv[cDev] == pThis)
339 {
340 pThis->pKeyboard->mpDrv[cDev] = NULL;
341 break;
342 }
343 pThis->pKeyboard->mpVMMDev = NULL;
344 }
345}
346
347/**
348 * Construct a keyboard driver instance.
349 *
350 * @copydoc FNPDMDRVCONSTRUCT
351 */
352DECLCALLBACK(int) Keyboard::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
353{
354 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
355 PDRVMAINKEYBOARD pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
356 LogFlow(("Keyboard::drvConstruct: iInstance=%d\n", pDrvIns->iInstance));
357
358 /*
359 * Validate configuration.
360 */
361 if (!CFGMR3AreValuesValid(pCfg, "Object\0"))
362 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
363 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
364 ("Configuration error: Not possible to attach anything to this driver!\n"),
365 VERR_PDM_DRVINS_NO_ATTACH);
366
367 /*
368 * IBase.
369 */
370 pDrvIns->IBase.pfnQueryInterface = Keyboard::drvQueryInterface;
371
372 pThis->IConnector.pfnLedStatusChange = keyboardLedStatusChange;
373 pThis->IConnector.pfnSetActive = Keyboard::keyboardSetActive;
374
375 /*
376 * Get the IKeyboardPort interface of the above driver/device.
377 */
378 pThis->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIKEYBOARDPORT);
379 if (!pThis->pUpPort)
380 {
381 AssertMsgFailed(("Configuration error: No keyboard port interface above!\n"));
382 return VERR_PDM_MISSING_INTERFACE_ABOVE;
383 }
384
385 /*
386 * Get the Keyboard object pointer and update the mpDrv member.
387 */
388 void *pv;
389 int rc = CFGMR3QueryPtr(pCfg, "Object", &pv);
390 if (RT_FAILURE(rc))
391 {
392 AssertMsgFailed(("Configuration error: No/bad \"Object\" value! rc=%Rrc\n", rc));
393 return rc;
394 }
395 pThis->pKeyboard = (Keyboard *)pv; /** @todo Check this cast! */
396 unsigned cDev;
397 for (cDev = 0; cDev < KEYBOARD_MAX_DEVICES; ++cDev)
398 if (!pThis->pKeyboard->mpDrv[cDev])
399 {
400 pThis->pKeyboard->mpDrv[cDev] = pThis;
401 break;
402 }
403 if (cDev == KEYBOARD_MAX_DEVICES)
404 return VERR_NO_MORE_HANDLES;
405
406 return VINF_SUCCESS;
407}
408
409
410/**
411 * Keyboard driver registration record.
412 */
413const PDMDRVREG Keyboard::DrvReg =
414{
415 /* u32Version */
416 PDM_DRVREG_VERSION,
417 /* szName */
418 "MainKeyboard",
419 /* szRCMod */
420 "",
421 /* szR0Mod */
422 "",
423 /* pszDescription */
424 "Main keyboard driver (Main as in the API).",
425 /* fFlags */
426 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
427 /* fClass. */
428 PDM_DRVREG_CLASS_KEYBOARD,
429 /* cMaxInstances */
430 ~0U,
431 /* cbInstance */
432 sizeof(DRVMAINKEYBOARD),
433 /* pfnConstruct */
434 Keyboard::drvConstruct,
435 /* pfnDestruct */
436 Keyboard::drvDestruct,
437 /* pfnRelocate */
438 NULL,
439 /* pfnIOCtl */
440 NULL,
441 /* pfnPowerOn */
442 NULL,
443 /* pfnReset */
444 NULL,
445 /* pfnSuspend */
446 NULL,
447 /* pfnResume */
448 NULL,
449 /* pfnAttach */
450 NULL,
451 /* pfnDetach */
452 NULL,
453 /* pfnPowerOff */
454 NULL,
455 /* pfnSoftReset */
456 NULL,
457 /* u32EndVersion */
458 PDM_DRVREG_VERSION
459};
460/* 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