VirtualBox

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

Last change on this file since 94521 was 93444, checked in by vboxsync, 2 years ago

VMM,Main,HostServices: Use a function table for accessing the VBoxVMM.dll/so/dylib functionality, and load it dynamically when the Console object is initialized. Also converted a few drivers in Main to use device helpers to get config values and such. bugref:10074

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.5 KB
Line 
1/* $Id: KeyboardImpl.cpp 93444 2022-01-26 18:01:15Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2022 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 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
299
300 /* Release all keys on the active keyboard in order to start with a clean slate.
301 * Note that this should mirror the logic in Keyboard::putScancodes() when choosing
302 * which keyboard to send the release event to.
303 */
304 PPDMIKEYBOARDPORT pUpPort = NULL;
305 for (int i = KEYBOARD_MAX_DEVICES - 1; i >= 0 ; --i)
306 {
307 if (mpDrv[i] && (mpDrv[i]->u32DevCaps & KEYBOARD_DEVCAP_ENABLED))
308 {
309 pUpPort = mpDrv[i]->pUpPort;
310 break;
311 }
312 }
313
314 if (pUpPort)
315 {
316 int rc = pUpPort->pfnReleaseKeys(pUpPort);
317 if (RT_FAILURE(rc))
318 AssertMsgFailed(("Failed to release keys on all keyboards! rc=%Rrc\n", rc));
319 }
320
321 return S_OK;
322}
323
324HRESULT Keyboard::getKeyboardLEDs(std::vector<KeyboardLED_T> &aKeyboardLEDs)
325{
326 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
327
328 aKeyboardLEDs.resize(0);
329
330 if (menmLeds & PDMKEYBLEDS_NUMLOCK) aKeyboardLEDs.push_back(KeyboardLED_NumLock);
331 if (menmLeds & PDMKEYBLEDS_CAPSLOCK) aKeyboardLEDs.push_back(KeyboardLED_CapsLock);
332 if (menmLeds & PDMKEYBLEDS_SCROLLLOCK) aKeyboardLEDs.push_back(KeyboardLED_ScrollLock);
333
334 return S_OK;
335}
336
337HRESULT Keyboard::getEventSource(ComPtr<IEventSource> &aEventSource)
338{
339 // No need to lock - lifetime constant
340 mEventSource.queryInterfaceTo(aEventSource.asOutParam());
341
342 return S_OK;
343}
344
345//
346// private methods
347//
348void Keyboard::onKeyboardLedsChange(PDMKEYBLEDS enmLeds)
349{
350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
351
352 /* Save the current status. */
353 menmLeds = enmLeds;
354
355 alock.release();
356
357 i_getParent()->i_onKeyboardLedsChange(RT_BOOL(enmLeds & PDMKEYBLEDS_NUMLOCK),
358 RT_BOOL(enmLeds & PDMKEYBLEDS_CAPSLOCK),
359 RT_BOOL(enmLeds & PDMKEYBLEDS_SCROLLLOCK));
360}
361
362DECLCALLBACK(void) Keyboard::i_keyboardLedStatusChange(PPDMIKEYBOARDCONNECTOR pInterface, PDMKEYBLEDS enmLeds)
363{
364 PDRVMAINKEYBOARD pDrv = RT_FROM_MEMBER(pInterface, DRVMAINKEYBOARD, IConnector);
365 pDrv->pKeyboard->onKeyboardLedsChange(enmLeds);
366}
367
368/**
369 * @interface_method_impl{PDMIKEYBOARDCONNECTOR,pfnSetActive}
370 */
371DECLCALLBACK(void) Keyboard::i_keyboardSetActive(PPDMIKEYBOARDCONNECTOR pInterface, bool fActive)
372{
373 PDRVMAINKEYBOARD pDrv = RT_FROM_MEMBER(pInterface, DRVMAINKEYBOARD, IConnector);
374
375 // Before activating a different keyboard, release all keys on the currently active one.
376 if (fActive)
377 pDrv->pKeyboard->releaseKeys();
378
379 if (fActive)
380 pDrv->u32DevCaps |= KEYBOARD_DEVCAP_ENABLED;
381 else
382 pDrv->u32DevCaps &= ~KEYBOARD_DEVCAP_ENABLED;
383}
384
385
386/**
387 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
388 */
389DECLCALLBACK(void *) Keyboard::i_drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
390{
391 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
392 PDRVMAINKEYBOARD pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
393
394 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
395 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIKEYBOARDCONNECTOR, &pDrv->IConnector);
396 return NULL;
397}
398
399
400/**
401 * Destruct a keyboard driver instance.
402 *
403 * @returns VBox status code.
404 * @param pDrvIns The driver instance data.
405 */
406DECLCALLBACK(void) Keyboard::i_drvDestruct(PPDMDRVINS pDrvIns)
407{
408 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
409 PDRVMAINKEYBOARD pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
410 LogFlow(("Keyboard::drvDestruct: iInstance=%d\n", pDrvIns->iInstance));
411
412 if (pThis->pKeyboard)
413 {
414 AutoWriteLock kbdLock(pThis->pKeyboard COMMA_LOCKVAL_SRC_POS);
415 for (unsigned cDev = 0; cDev < KEYBOARD_MAX_DEVICES; ++cDev)
416 if (pThis->pKeyboard->mpDrv[cDev] == pThis)
417 {
418 pThis->pKeyboard->mpDrv[cDev] = NULL;
419 break;
420 }
421 }
422}
423
424/**
425 * Construct a keyboard driver instance.
426 *
427 * @copydoc FNPDMDRVCONSTRUCT
428 */
429DECLCALLBACK(int) Keyboard::i_drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
430{
431 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
432 RT_NOREF(fFlags, pCfg);
433 PDRVMAINKEYBOARD pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
434 LogFlow(("Keyboard::drvConstruct: iInstance=%d\n", pDrvIns->iInstance));
435
436 /*
437 * Validate configuration.
438 */
439 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "", "");
440 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
441 ("Configuration error: Not possible to attach anything to this driver!\n"),
442 VERR_PDM_DRVINS_NO_ATTACH);
443
444 /*
445 * IBase.
446 */
447 pDrvIns->IBase.pfnQueryInterface = Keyboard::i_drvQueryInterface;
448
449 pThis->IConnector.pfnLedStatusChange = i_keyboardLedStatusChange;
450 pThis->IConnector.pfnSetActive = Keyboard::i_keyboardSetActive;
451
452 /*
453 * Get the IKeyboardPort interface of the above driver/device.
454 */
455 pThis->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIKEYBOARDPORT);
456 if (!pThis->pUpPort)
457 {
458 AssertMsgFailed(("Configuration error: No keyboard port interface above!\n"));
459 return VERR_PDM_MISSING_INTERFACE_ABOVE;
460 }
461
462 /*
463 * Get the Keyboard object pointer and update the mpDrv member.
464 */
465 com::Guid uuid(COM_IIDOF(IKeyboard));
466 IKeyboard *pIKeyboard = (IKeyboard *)PDMDrvHlpQueryGenericUserObject(pDrvIns, uuid.raw());
467 if (!pIKeyboard)
468 {
469 AssertMsgFailed(("Configuration error: No/bad Keyboard object!\n"));
470 return VERR_NOT_FOUND;
471 }
472 pThis->pKeyboard = static_cast<Keyboard *>(pIKeyboard);
473
474 unsigned cDev;
475 for (cDev = 0; cDev < KEYBOARD_MAX_DEVICES; ++cDev)
476 if (!pThis->pKeyboard->mpDrv[cDev])
477 {
478 pThis->pKeyboard->mpDrv[cDev] = pThis;
479 break;
480 }
481 if (cDev == KEYBOARD_MAX_DEVICES)
482 return VERR_NO_MORE_HANDLES;
483
484 return VINF_SUCCESS;
485}
486
487
488/**
489 * Keyboard driver registration record.
490 */
491const PDMDRVREG Keyboard::DrvReg =
492{
493 /* u32Version */
494 PDM_DRVREG_VERSION,
495 /* szName */
496 "MainKeyboard",
497 /* szRCMod */
498 "",
499 /* szR0Mod */
500 "",
501 /* pszDescription */
502 "Main keyboard driver (Main as in the API).",
503 /* fFlags */
504 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
505 /* fClass. */
506 PDM_DRVREG_CLASS_KEYBOARD,
507 /* cMaxInstances */
508 ~0U,
509 /* cbInstance */
510 sizeof(DRVMAINKEYBOARD),
511 /* pfnConstruct */
512 Keyboard::i_drvConstruct,
513 /* pfnDestruct */
514 Keyboard::i_drvDestruct,
515 /* pfnRelocate */
516 NULL,
517 /* pfnIOCtl */
518 NULL,
519 /* pfnPowerOn */
520 NULL,
521 /* pfnReset */
522 NULL,
523 /* pfnSuspend */
524 NULL,
525 /* pfnResume */
526 NULL,
527 /* pfnAttach */
528 NULL,
529 /* pfnDetach */
530 NULL,
531 /* pfnPowerOff */
532 NULL,
533 /* pfnSoftReset */
534 NULL,
535 /* u32EndVersion */
536 PDM_DRVREG_VERSION
537};
538/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use