VirtualBox

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

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

Send HID usage codes rather than XT scan codes to keyboard devices, with conversion in driver. See #6026.

  • 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 50962 2014-04-03 13:58:47Z 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();
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->pfnPutEventScan(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.

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