VirtualBox

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

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

*: doxygen fixes

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