VirtualBox

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

Last change on this file since 86728 was 85300, checked in by vboxsync, 4 years ago

Main/comimpl.xsl,++: fireXxxxEvent -> ::FireXxxxEvent. bugref:9790

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