VirtualBox

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

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

Main: use RT_FROM_MEMBER instead of RT_OFFSETOF; some Assert* cleanup

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