VirtualBox

source: vbox/trunk/src/VBox/Main/KeyboardImpl.cpp@ 33060

Last change on this file since 33060 was 32718, checked in by vboxsync, 14 years ago

com/string: Remove bool conversion operator and other convenience error operators. They are hiding programming errors (like incorrect empty string checks, and in one case a free of the wrong pointer).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.0 KB
Line 
1/* $Id: KeyboardImpl.cpp 32718 2010-09-23 12:57:52Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2010 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/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 S_OK;
85}
86
87void Keyboard::FinalRelease()
88{
89 uninit();
90}
91
92// public methods
93////////////////////////////////////////////////////////////////////////////////
94
95/**
96 * Initializes the keyboard object.
97 *
98 * @returns COM result indicator
99 * @param parent handle of our parent object
100 */
101HRESULT Keyboard::init(Console *aParent)
102{
103 LogFlowThisFunc(("aParent=%p\n", aParent));
104
105 ComAssertRet(aParent, E_INVALIDARG);
106
107 /* Enclose the state transition NotReady->InInit->Ready */
108 AutoInitSpan autoInitSpan(this);
109 AssertReturn(autoInitSpan.isOk(), E_FAIL);
110
111 unconst(mParent) = aParent;
112
113 /* Confirm a successful initialization */
114 autoInitSpan.setSucceeded();
115
116 return S_OK;
117}
118
119/**
120 * Uninitializes the instance and sets the ready flag to FALSE.
121 * Called either from FinalRelease() or by the parent when it gets destroyed.
122 */
123void Keyboard::uninit()
124{
125 LogFlowThisFunc(("\n"));
126
127 /* Enclose the state transition Ready->InUninit->NotReady */
128 AutoUninitSpan autoUninitSpan(this);
129 if (autoUninitSpan.uninitDone())
130 return;
131
132 for (unsigned i = 0; i < KEYBOARD_MAX_DEVICES; ++i)
133 {
134 if (mpDrv[i])
135 mpDrv[i]->pKeyboard = NULL;
136 mpDrv[i] = NULL;
137 }
138
139 mpVMMDev = NULL;
140 mfVMMDevInited = true;
141
142 unconst(mParent) = NULL;
143}
144
145/**
146 * Sends a scancode to the keyboard.
147 *
148 * @returns COM status code
149 * @param scancode The scancode to send
150 */
151STDMETHODIMP Keyboard::PutScancode(LONG scancode)
152{
153 HRESULT rc = S_OK;
154
155 AutoCaller autoCaller(this);
156 if (FAILED(autoCaller.rc())) return autoCaller.rc();
157
158 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
159
160 CHECK_CONSOLE_DRV(mpDrv[0]);
161
162 PPDMIKEYBOARDPORT pUpPort = NULL;
163 for (int i = KEYBOARD_MAX_DEVICES - 1; i >= 0 ; --i)
164 {
165 if (mpDrv[i] && (mpDrv[i]->u32DevCaps & KEYBOARD_DEVCAP_ENABLED))
166 {
167 pUpPort = mpDrv[i]->pUpPort;
168 break;
169 }
170 }
171 /* No enabled keyboard - throw the input away. */
172 if (!pUpPort)
173 return rc;
174
175 int vrc = pUpPort->pfnPutEvent(pUpPort, (uint8_t)scancode);
176
177 if (RT_FAILURE(vrc))
178 rc = setError(VBOX_E_IPRT_ERROR,
179 tr("Could not send scan code 0x%08X to the virtual keyboard (%Rrc)"),
180 scancode, vrc);
181
182 return rc;
183}
184
185/**
186 * Sends a list of scancodes to the keyboard.
187 *
188 * @returns COM status code
189 * @param scancodes Pointer to the first scancode
190 * @param count Number of scancodes
191 * @param codesStored Address of variable to store the number
192 * of scancodes that were sent to the keyboard.
193 This value can be NULL.
194 */
195STDMETHODIMP Keyboard::PutScancodes(ComSafeArrayIn(LONG, scancodes),
196 ULONG *codesStored)
197{
198 HRESULT rc = S_OK;
199
200 if (ComSafeArrayInIsNull(scancodes))
201 return E_INVALIDARG;
202
203 AutoCaller autoCaller(this);
204 if (FAILED(autoCaller.rc())) return autoCaller.rc();
205
206 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
207
208 CHECK_CONSOLE_DRV(mpDrv[0]);
209
210 /* Send input to the last enabled device. Relies on the fact that
211 * the USB keyboard is always initialized after the PS/2 keyboard.
212 */
213 PPDMIKEYBOARDPORT pUpPort = NULL;
214 for (int i = KEYBOARD_MAX_DEVICES - 1; i >= 0 ; --i)
215 {
216 if (mpDrv[i] && (mpDrv[i]->u32DevCaps & KEYBOARD_DEVCAP_ENABLED))
217 {
218 pUpPort = mpDrv[i]->pUpPort;
219 break;
220 }
221 }
222 /* No enabled keyboard - throw the input away. */
223 if (!pUpPort)
224 return rc;
225
226 com::SafeArray<LONG> keys(ComSafeArrayInArg(scancodes));
227 int vrc = VINF_SUCCESS;
228
229 for (uint32_t i = 0; (i < keys.size()) && RT_SUCCESS(vrc); i++)
230 vrc = pUpPort->pfnPutEvent(pUpPort, (uint8_t)keys[i]);
231
232 if (RT_FAILURE(vrc))
233 return setError(VBOX_E_IPRT_ERROR,
234 tr("Could not send all scan codes to the virtual keyboard (%Rrc)"),
235 vrc);
236
237 /// @todo is it actually possible that not all scancodes can be transmitted?
238 if (codesStored)
239 *codesStored = (uint32_t)keys.size();
240
241 return rc;
242}
243
244/**
245 * Sends Control-Alt-Delete to the keyboard. This could be done otherwise
246 * but it's so common that we'll be nice and supply a convenience API.
247 *
248 * @returns COM status code
249 *
250 */
251STDMETHODIMP Keyboard::PutCAD()
252{
253 static com::SafeArray<LONG> cadSequence(6);
254
255 cadSequence[0] = 0x1d; // Ctrl down
256 cadSequence[1] = 0x38; // Alt down
257 cadSequence[2] = 0x53; // Del down
258 cadSequence[3] = 0xd3; // Del up
259 cadSequence[4] = 0xb8; // Alt up
260 cadSequence[5] = 0x9d; // Ctrl up
261
262 return PutScancodes(ComSafeArrayAsInParam(cadSequence), NULL);
263}
264
265//
266// private methods
267//
268
269/**
270 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
271 */
272DECLCALLBACK(void *) Keyboard::drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
273{
274 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
275 PDRVMAINKEYBOARD pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
276
277 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
278 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIKEYBOARDCONNECTOR, &pDrv->IConnector);
279 return NULL;
280}
281
282
283/**
284 * Destruct a keyboard driver instance.
285 *
286 * @returns VBox status.
287 * @param pDrvIns The driver instance data.
288 */
289DECLCALLBACK(void) Keyboard::drvDestruct(PPDMDRVINS pDrvIns)
290{
291 PDRVMAINKEYBOARD pData = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
292 LogFlow(("Keyboard::drvDestruct: iInstance=%d\n", pDrvIns->iInstance));
293 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
294
295 if (pData->pKeyboard)
296 {
297 AutoWriteLock kbdLock(pData->pKeyboard COMMA_LOCKVAL_SRC_POS);
298 for (unsigned cDev = 0; cDev < KEYBOARD_MAX_DEVICES; ++cDev)
299 if (pData->pKeyboard->mpDrv[cDev] == pData)
300 {
301 pData->pKeyboard->mpDrv[cDev] = NULL;
302 break;
303 }
304 pData->pKeyboard->mpVMMDev = NULL;
305 }
306}
307
308DECLCALLBACK(void) keyboardLedStatusChange(PPDMIKEYBOARDCONNECTOR pInterface,
309 PDMKEYBLEDS enmLeds)
310{
311 PDRVMAINKEYBOARD pDrv = PPDMIKEYBOARDCONNECTOR_2_MAINKEYBOARD(pInterface);
312 pDrv->pKeyboard->getParent()->onKeyboardLedsChange(!!(enmLeds & PDMKEYBLEDS_NUMLOCK),
313 !!(enmLeds & PDMKEYBLEDS_CAPSLOCK),
314 !!(enmLeds & PDMKEYBLEDS_SCROLLLOCK));
315}
316
317/**
318 * @interface_method_impl{PDMIKEYBOARDCONNECTOR,pfnSetActive}
319 */
320DECLCALLBACK(void) Keyboard::keyboardSetActive(PPDMIKEYBOARDCONNECTOR pInterface, bool fActive)
321{
322 PDRVMAINKEYBOARD pDrv = PPDMIKEYBOARDCONNECTOR_2_MAINKEYBOARD(pInterface);
323 if (fActive)
324 pDrv->u32DevCaps |= KEYBOARD_DEVCAP_ENABLED;
325 else
326 pDrv->u32DevCaps &= ~KEYBOARD_DEVCAP_ENABLED;
327}
328
329/**
330 * Construct a keyboard driver instance.
331 *
332 * @copydoc FNPDMDRVCONSTRUCT
333 */
334DECLCALLBACK(int) Keyboard::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg,
335 uint32_t fFlags)
336{
337 PDRVMAINKEYBOARD pData = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
338 LogFlow(("Keyboard::drvConstruct: iInstance=%d\n", pDrvIns->iInstance));
339 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
340
341 /*
342 * Validate configuration.
343 */
344 if (!CFGMR3AreValuesValid(pCfg, "Object\0"))
345 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
346 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
347 ("Configuration error: Not possible to attach anything to this driver!\n"),
348 VERR_PDM_DRVINS_NO_ATTACH);
349
350 /*
351 * IBase.
352 */
353 pDrvIns->IBase.pfnQueryInterface = Keyboard::drvQueryInterface;
354
355 pData->IConnector.pfnLedStatusChange = keyboardLedStatusChange;
356 pData->IConnector.pfnSetActive = keyboardSetActive;
357
358 /*
359 * Get the IKeyboardPort interface of the above driver/device.
360 */
361 pData->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIKEYBOARDPORT);
362 if (!pData->pUpPort)
363 {
364 AssertMsgFailed(("Configuration error: No keyboard port interface above!\n"));
365 return VERR_PDM_MISSING_INTERFACE_ABOVE;
366 }
367
368 /*
369 * Get the Keyboard object pointer and update the mpDrv member.
370 */
371 void *pv;
372 int rc = CFGMR3QueryPtr(pCfg, "Object", &pv);
373 if (RT_FAILURE(rc))
374 {
375 AssertMsgFailed(("Configuration error: No/bad \"Object\" value! rc=%Rrc\n", rc));
376 return rc;
377 }
378 pData->pKeyboard = (Keyboard *)pv; /** @todo Check this cast! */
379 unsigned cDev;
380 for (cDev = 0; cDev < KEYBOARD_MAX_DEVICES; ++cDev)
381 if (!pData->pKeyboard->mpDrv[cDev])
382 {
383 pData->pKeyboard->mpDrv[cDev] = pData;
384 break;
385 }
386 if (cDev == KEYBOARD_MAX_DEVICES)
387 return VERR_NO_MORE_HANDLES;
388
389 return VINF_SUCCESS;
390}
391
392
393/**
394 * Keyboard driver registration record.
395 */
396const PDMDRVREG Keyboard::DrvReg =
397{
398 /* u32Version */
399 PDM_DRVREG_VERSION,
400 /* szName */
401 "MainKeyboard",
402 /* szRCMod */
403 "",
404 /* szR0Mod */
405 "",
406 /* pszDescription */
407 "Main keyboard driver (Main as in the API).",
408 /* fFlags */
409 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
410 /* fClass. */
411 PDM_DRVREG_CLASS_KEYBOARD,
412 /* cMaxInstances */
413 ~0,
414 /* cbInstance */
415 sizeof(DRVMAINKEYBOARD),
416 /* pfnConstruct */
417 Keyboard::drvConstruct,
418 /* pfnDestruct */
419 Keyboard::drvDestruct,
420 /* pfnRelocate */
421 NULL,
422 /* pfnIOCtl */
423 NULL,
424 /* pfnPowerOn */
425 NULL,
426 /* pfnReset */
427 NULL,
428 /* pfnSuspend */
429 NULL,
430 /* pfnResume */
431 NULL,
432 /* pfnAttach */
433 NULL,
434 /* pfnDetach */
435 NULL,
436 /* pfnPowerOff */
437 NULL,
438 /* pfnSoftReset */
439 NULL,
440 /* u32EndVersion */
441 PDM_DRVREG_VERSION
442};
443/* 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