VirtualBox

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

Last change on this file since 81355 was 81246, checked in by vboxsync, 5 years ago

Main: keyboard HID usage ID is 16-bit.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.9 KB
Line 
1/* $Id: KeyboardImpl.cpp 81246 2019-10-14 10:00:06Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2019 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#define LOG_GROUP LOG_GROUP_MAIN_KEYBOARD
19#include "LoggingNew.h"
20
21#include "KeyboardImpl.h"
22#include "ConsoleImpl.h"
23
24#include "AutoCaller.h"
25
26#include <VBox/com/array.h>
27#include <VBox/vmm/pdmdrv.h>
28#include <VBox/err.h>
29
30#include <iprt/cpp/utils.h>
31
32
33// defines
34////////////////////////////////////////////////////////////////////////////////
35
36// globals
37////////////////////////////////////////////////////////////////////////////////
38
39/** @name Keyboard device capabilities bitfield
40 * @{ */
41enum
42{
43 /** The keyboard device does not wish to receive keystrokes. */
44 KEYBOARD_DEVCAP_DISABLED = 0,
45 /** The keyboard device does wishes to receive keystrokes. */
46 KEYBOARD_DEVCAP_ENABLED = 1
47};
48
49/**
50 * Keyboard driver instance data.
51 */
52typedef struct DRVMAINKEYBOARD
53{
54 /** Pointer to the keyboard object. */
55 Keyboard *pKeyboard;
56 /** Pointer to the driver instance structure. */
57 PPDMDRVINS pDrvIns;
58 /** Pointer to the keyboard port interface of the driver/device above us. */
59 PPDMIKEYBOARDPORT pUpPort;
60 /** Our keyboard connector interface. */
61 PDMIKEYBOARDCONNECTOR IConnector;
62 /** The capabilities of this device. */
63 uint32_t u32DevCaps;
64} DRVMAINKEYBOARD, *PDRVMAINKEYBOARD;
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 menmLeds = PDMKEYBLEDS_NONE;
83 return BaseFinalConstruct();
84}
85
86void Keyboard::FinalRelease()
87{
88 uninit();
89 BaseFinalRelease();
90}
91
92// public methods
93////////////////////////////////////////////////////////////////////////////////
94
95/**
96 * Initializes the keyboard object.
97 *
98 * @returns COM result indicator
99 * @param aParent 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 unconst(mEventSource).createObject();
114 HRESULT rc = mEventSource->init();
115 AssertComRCReturnRC(rc);
116
117 /* Confirm a successful initialization */
118 autoInitSpan.setSucceeded();
119
120 return S_OK;
121}
122
123/**
124 * Uninitializes the instance and sets the ready flag to FALSE.
125 * Called either from FinalRelease() or by the parent when it gets destroyed.
126 */
127void Keyboard::uninit()
128{
129 LogFlowThisFunc(("\n"));
130
131 /* Enclose the state transition Ready->InUninit->NotReady */
132 AutoUninitSpan autoUninitSpan(this);
133 if (autoUninitSpan.uninitDone())
134 return;
135
136 for (unsigned i = 0; i < KEYBOARD_MAX_DEVICES; ++i)
137 {
138 if (mpDrv[i])
139 mpDrv[i]->pKeyboard = NULL;
140 mpDrv[i] = NULL;
141 }
142
143 menmLeds = PDMKEYBLEDS_NONE;
144
145 unconst(mParent) = NULL;
146 unconst(mEventSource).setNull();
147}
148
149/**
150 * Sends a scancode to the keyboard.
151 *
152 * @returns COM status code
153 * @param aScancode The scancode to send
154 */
155HRESULT Keyboard::putScancode(LONG aScancode)
156{
157 std::vector<LONG> scancodes;
158 scancodes.resize(1);
159 scancodes[0] = aScancode;
160 return putScancodes(scancodes, NULL);
161}
162
163/**
164 * Sends a list of scancodes to the keyboard.
165 *
166 * @returns COM status code
167 * @param aScancodes Pointer to the first scancode
168 * @param aCodesStored Address of variable to store the number
169 * of scancodes that were sent to the keyboard.
170 This value can be NULL.
171 */
172HRESULT Keyboard::putScancodes(const std::vector<LONG> &aScancodes,
173 ULONG *aCodesStored)
174{
175 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
176
177 CHECK_CONSOLE_DRV(mpDrv[0]);
178
179 /* Send input to the last enabled device. Relies on the fact that
180 * the USB keyboard is always initialized after the PS/2 keyboard.
181 */
182 PPDMIKEYBOARDPORT pUpPort = NULL;
183 for (int i = KEYBOARD_MAX_DEVICES - 1; i >= 0 ; --i)
184 {
185 if (mpDrv[i] && (mpDrv[i]->u32DevCaps & KEYBOARD_DEVCAP_ENABLED))
186 {
187 pUpPort = mpDrv[i]->pUpPort;
188 break;
189 }
190 }
191
192 /* No enabled keyboard - throw the input away. */
193 if (!pUpPort)
194 {
195 if (aCodesStored)
196 *aCodesStored = (uint32_t)aScancodes.size();
197 return S_OK;
198 }
199
200 int vrc = VINF_SUCCESS;
201
202 uint32_t sent;
203 for (sent = 0; (sent < aScancodes.size()) && RT_SUCCESS(vrc); ++sent)
204 vrc = pUpPort->pfnPutEventScan(pUpPort, (uint8_t)aScancodes[sent]);
205
206 if (aCodesStored)
207 *aCodesStored = sent;
208
209 com::SafeArray<LONG> keys(aScancodes.size());
210 for (size_t i = 0; i < aScancodes.size(); ++i)
211 keys[i] = aScancodes[i];
212
213 VBoxEventDesc evDesc;
214 evDesc.init(mEventSource, VBoxEventType_OnGuestKeyboard, ComSafeArrayAsInParam(keys));
215 evDesc.fire(0);
216
217 if (RT_FAILURE(vrc))
218 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
219 tr("Could not send all scan codes to the virtual keyboard (%Rrc)"),
220 vrc);
221
222 return S_OK;
223}
224
225/**
226 * Sends a HID usage code and page to the keyboard.
227 *
228 * @returns COM status code
229 * @param aUsageCode The HID usage code to send
230 * @param aUsagePage The HID usage page corresponding to the code
231 * @param fKeyRelease The key release flag
232 */
233HRESULT Keyboard::putUsageCode(LONG aUsageCode, LONG aUsagePage, BOOL fKeyRelease)
234{
235 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
236
237 CHECK_CONSOLE_DRV(mpDrv[0]);
238
239 /* Send input to the last enabled device. Relies on the fact that
240 * the USB keyboard is always initialized after the PS/2 keyboard.
241 */
242 PPDMIKEYBOARDPORT pUpPort = NULL;
243 for (int i = KEYBOARD_MAX_DEVICES - 1; i >= 0 ; --i)
244 {
245 if (mpDrv[i] && (mpDrv[i]->u32DevCaps & KEYBOARD_DEVCAP_ENABLED))
246 {
247 pUpPort = mpDrv[i]->pUpPort;
248 break;
249 }
250 }
251
252 /* No enabled keyboard - throw the input away. */
253 if (!pUpPort)
254 return S_OK;
255
256 int vrc = VINF_SUCCESS;
257 uint32_t u32Usage;
258 u32Usage = (uint16_t)aUsageCode | ((uint32_t)(uint8_t)aUsagePage << 16) | (fKeyRelease ? 0x80000000 : 0);
259 vrc = pUpPort->pfnPutEventHid(pUpPort, u32Usage);
260 if (RT_FAILURE(vrc))
261 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
262 tr("Could not send usage code to the virtual keyboard (%Rrc)"),
263 vrc);
264
265 return S_OK;
266}
267
268/**
269 * Sends Control-Alt-Delete to the keyboard. This could be done otherwise
270 * but it's so common that we'll be nice and supply a convenience API.
271 *
272 * @returns COM status code
273 *
274 */
275HRESULT Keyboard::putCAD()
276{
277 std::vector<LONG> cadSequence;
278 cadSequence.resize(8);
279
280 cadSequence[0] = 0x1d; // Ctrl down
281 cadSequence[1] = 0x38; // Alt down
282 cadSequence[2] = 0xe0; // Del down 1
283 cadSequence[3] = 0x53; // Del down 2
284 cadSequence[4] = 0xe0; // Del up 1
285 cadSequence[5] = 0xd3; // Del up 2
286 cadSequence[6] = 0xb8; // Alt up
287 cadSequence[7] = 0x9d; // Ctrl up
288
289 return putScancodes(cadSequence, NULL);
290}
291
292/**
293 * Releases all currently held keys in the virtual keyboard.
294 *
295 * @returns COM status code
296 *
297 */
298HRESULT Keyboard::releaseKeys()
299{
300 std::vector<LONG> scancodes;
301 scancodes.resize(1);
302 scancodes[0] = 0xFC; /* Magic scancode, see PS/2 and USB keyboard devices. */
303 return putScancodes(scancodes, NULL);
304}
305
306HRESULT Keyboard::getKeyboardLEDs(std::vector<KeyboardLED_T> &aKeyboardLEDs)
307{
308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
309
310 aKeyboardLEDs.resize(0);
311
312 if (menmLeds & PDMKEYBLEDS_NUMLOCK) aKeyboardLEDs.push_back(KeyboardLED_NumLock);
313 if (menmLeds & PDMKEYBLEDS_CAPSLOCK) aKeyboardLEDs.push_back(KeyboardLED_CapsLock);
314 if (menmLeds & PDMKEYBLEDS_SCROLLLOCK) aKeyboardLEDs.push_back(KeyboardLED_ScrollLock);
315
316 return S_OK;
317}
318
319HRESULT Keyboard::getEventSource(ComPtr<IEventSource> &aEventSource)
320{
321 // No need to lock - lifetime constant
322 mEventSource.queryInterfaceTo(aEventSource.asOutParam());
323
324 return S_OK;
325}
326
327//
328// private methods
329//
330void Keyboard::onKeyboardLedsChange(PDMKEYBLEDS enmLeds)
331{
332 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
333
334 /* Save the current status. */
335 menmLeds = enmLeds;
336
337 alock.release();
338
339 i_getParent()->i_onKeyboardLedsChange(RT_BOOL(enmLeds & PDMKEYBLEDS_NUMLOCK),
340 RT_BOOL(enmLeds & PDMKEYBLEDS_CAPSLOCK),
341 RT_BOOL(enmLeds & PDMKEYBLEDS_SCROLLLOCK));
342}
343
344DECLCALLBACK(void) Keyboard::i_keyboardLedStatusChange(PPDMIKEYBOARDCONNECTOR pInterface, PDMKEYBLEDS enmLeds)
345{
346 PDRVMAINKEYBOARD pDrv = RT_FROM_MEMBER(pInterface, DRVMAINKEYBOARD, IConnector);
347 pDrv->pKeyboard->onKeyboardLedsChange(enmLeds);
348}
349
350/**
351 * @interface_method_impl{PDMIKEYBOARDCONNECTOR,pfnSetActive}
352 */
353DECLCALLBACK(void) Keyboard::i_keyboardSetActive(PPDMIKEYBOARDCONNECTOR pInterface, bool fActive)
354{
355 PDRVMAINKEYBOARD pDrv = RT_FROM_MEMBER(pInterface, DRVMAINKEYBOARD, IConnector);
356 if (fActive)
357 pDrv->u32DevCaps |= KEYBOARD_DEVCAP_ENABLED;
358 else
359 pDrv->u32DevCaps &= ~KEYBOARD_DEVCAP_ENABLED;
360}
361
362
363/**
364 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
365 */
366DECLCALLBACK(void *) Keyboard::i_drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
367{
368 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
369 PDRVMAINKEYBOARD pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
370
371 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
372 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIKEYBOARDCONNECTOR, &pDrv->IConnector);
373 return NULL;
374}
375
376
377/**
378 * Destruct a keyboard driver instance.
379 *
380 * @returns VBox status code.
381 * @param pDrvIns The driver instance data.
382 */
383DECLCALLBACK(void) Keyboard::i_drvDestruct(PPDMDRVINS pDrvIns)
384{
385 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
386 PDRVMAINKEYBOARD pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
387 LogFlow(("Keyboard::drvDestruct: iInstance=%d\n", pDrvIns->iInstance));
388
389 if (pThis->pKeyboard)
390 {
391 AutoWriteLock kbdLock(pThis->pKeyboard COMMA_LOCKVAL_SRC_POS);
392 for (unsigned cDev = 0; cDev < KEYBOARD_MAX_DEVICES; ++cDev)
393 if (pThis->pKeyboard->mpDrv[cDev] == pThis)
394 {
395 pThis->pKeyboard->mpDrv[cDev] = NULL;
396 break;
397 }
398 }
399}
400
401/**
402 * Construct a keyboard driver instance.
403 *
404 * @copydoc FNPDMDRVCONSTRUCT
405 */
406DECLCALLBACK(int) Keyboard::i_drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
407{
408 RT_NOREF(fFlags);
409 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
410 PDRVMAINKEYBOARD pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
411 LogFlow(("Keyboard::drvConstruct: iInstance=%d\n", pDrvIns->iInstance));
412
413 /*
414 * Validate configuration.
415 */
416 if (!CFGMR3AreValuesValid(pCfg, "Object\0"))
417 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
418 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
419 ("Configuration error: Not possible to attach anything to this driver!\n"),
420 VERR_PDM_DRVINS_NO_ATTACH);
421
422 /*
423 * IBase.
424 */
425 pDrvIns->IBase.pfnQueryInterface = Keyboard::i_drvQueryInterface;
426
427 pThis->IConnector.pfnLedStatusChange = i_keyboardLedStatusChange;
428 pThis->IConnector.pfnSetActive = Keyboard::i_keyboardSetActive;
429
430 /*
431 * Get the IKeyboardPort interface of the above driver/device.
432 */
433 pThis->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIKEYBOARDPORT);
434 if (!pThis->pUpPort)
435 {
436 AssertMsgFailed(("Configuration error: No keyboard port interface above!\n"));
437 return VERR_PDM_MISSING_INTERFACE_ABOVE;
438 }
439
440 /*
441 * Get the Keyboard object pointer and update the mpDrv member.
442 */
443 void *pv;
444 int rc = CFGMR3QueryPtr(pCfg, "Object", &pv);
445 if (RT_FAILURE(rc))
446 {
447 AssertMsgFailed(("Configuration error: No/bad \"Object\" value! rc=%Rrc\n", rc));
448 return rc;
449 }
450 pThis->pKeyboard = (Keyboard *)pv; /** @todo Check this cast! */
451 unsigned cDev;
452 for (cDev = 0; cDev < KEYBOARD_MAX_DEVICES; ++cDev)
453 if (!pThis->pKeyboard->mpDrv[cDev])
454 {
455 pThis->pKeyboard->mpDrv[cDev] = pThis;
456 break;
457 }
458 if (cDev == KEYBOARD_MAX_DEVICES)
459 return VERR_NO_MORE_HANDLES;
460
461 return VINF_SUCCESS;
462}
463
464
465/**
466 * Keyboard driver registration record.
467 */
468const PDMDRVREG Keyboard::DrvReg =
469{
470 /* u32Version */
471 PDM_DRVREG_VERSION,
472 /* szName */
473 "MainKeyboard",
474 /* szRCMod */
475 "",
476 /* szR0Mod */
477 "",
478 /* pszDescription */
479 "Main keyboard driver (Main as in the API).",
480 /* fFlags */
481 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
482 /* fClass. */
483 PDM_DRVREG_CLASS_KEYBOARD,
484 /* cMaxInstances */
485 ~0U,
486 /* cbInstance */
487 sizeof(DRVMAINKEYBOARD),
488 /* pfnConstruct */
489 Keyboard::i_drvConstruct,
490 /* pfnDestruct */
491 Keyboard::i_drvDestruct,
492 /* pfnRelocate */
493 NULL,
494 /* pfnIOCtl */
495 NULL,
496 /* pfnPowerOn */
497 NULL,
498 /* pfnReset */
499 NULL,
500 /* pfnSuspend */
501 NULL,
502 /* pfnResume */
503 NULL,
504 /* pfnAttach */
505 NULL,
506 /* pfnDetach */
507 NULL,
508 /* pfnPowerOff */
509 NULL,
510 /* pfnSoftReset */
511 NULL,
512 /* u32EndVersion */
513 PDM_DRVREG_VERSION
514};
515/* 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