VirtualBox

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

Last change on this file was 99739, checked in by vboxsync, 12 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.8 KB
RevLine 
[13607]1/* $Id: KeyboardImpl.cpp 99739 2023-05-11 01:01:08Z vboxsync $ */
[1]2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
[98103]7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
[1]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
[1]26 */
27
[67914]28#define LOG_GROUP LOG_GROUP_MAIN_KEYBOARD
29#include "LoggingNew.h"
30
[1]31#include "KeyboardImpl.h"
32#include "ConsoleImpl.h"
33
[25860]34#include "AutoCaller.h"
[85286]35#include "VBoxEvents.h"
[1]36
[11661]37#include <VBox/com/array.h>
[35346]38#include <VBox/vmm/pdmdrv.h>
[76760]39#include <VBox/err.h>
[30681]40
41#include <iprt/cpp/utils.h>
[1]42
[76760]43
[1]44// defines
45////////////////////////////////////////////////////////////////////////////////
46
47// globals
48////////////////////////////////////////////////////////////////////////////////
49
[81369]50/**
51 * Keyboard device capabilities bitfield.
52 */
[28909]53enum
54{
55 /** The keyboard device does not wish to receive keystrokes. */
56 KEYBOARD_DEVCAP_DISABLED = 0,
57 /** The keyboard device does wishes to receive keystrokes. */
58 KEYBOARD_DEVCAP_ENABLED = 1
59};
60
[1]61/**
62 * Keyboard driver instance data.
63 */
64typedef struct DRVMAINKEYBOARD
65{
66 /** Pointer to the keyboard object. */
67 Keyboard *pKeyboard;
68 /** Pointer to the driver instance structure. */
69 PPDMDRVINS pDrvIns;
70 /** Pointer to the keyboard port interface of the driver/device above us. */
71 PPDMIKEYBOARDPORT pUpPort;
[28909]72 /** Our keyboard connector interface. */
[25985]73 PDMIKEYBOARDCONNECTOR IConnector;
[28909]74 /** The capabilities of this device. */
75 uint32_t u32DevCaps;
[1]76} DRVMAINKEYBOARD, *PDRVMAINKEYBOARD;
77
78
79// constructor / destructor
80////////////////////////////////////////////////////////////////////////////////
81
[27608]82Keyboard::Keyboard()
83 : mParent(NULL)
84{
85}
[13606]86
[27608]87Keyboard::~Keyboard()
88{
89}
90
[1]91HRESULT Keyboard::FinalConstruct()
92{
[28909]93 RT_ZERO(mpDrv);
[52924]94 menmLeds = PDMKEYBLEDS_NONE;
[35638]95 return BaseFinalConstruct();
[1]96}
97
98void Keyboard::FinalRelease()
99{
[13606]100 uninit();
[35638]101 BaseFinalRelease();
[1]102}
103
104// public methods
105////////////////////////////////////////////////////////////////////////////////
106
107/**
108 * Initializes the keyboard object.
109 *
110 * @returns COM result indicator
[65120]111 * @param aParent handle of our parent object
[1]112 */
[32718]113HRESULT Keyboard::init(Console *aParent)
[1]114{
[21878]115 LogFlowThisFunc(("aParent=%p\n", aParent));
[1]116
[26235]117 ComAssertRet(aParent, E_INVALIDARG);
[1]118
[13606]119 /* Enclose the state transition NotReady->InInit->Ready */
[21878]120 AutoInitSpan autoInitSpan(this);
121 AssertReturn(autoInitSpan.isOk(), E_FAIL);
[1]122
[21878]123 unconst(mParent) = aParent;
[1]124
[33061]125 unconst(mEventSource).createObject();
[94923]126 HRESULT hrc = mEventSource->init();
127 AssertComRCReturnRC(hrc);
[33061]128
[13606]129 /* Confirm a successful initialization */
130 autoInitSpan.setSucceeded();
131
[1]132 return S_OK;
133}
134
135/**
136 * Uninitializes the instance and sets the ready flag to FALSE.
137 * Called either from FinalRelease() or by the parent when it gets destroyed.
138 */
139void Keyboard::uninit()
140{
[21878]141 LogFlowThisFunc(("\n"));
[1]142
[13606]143 /* Enclose the state transition Ready->InUninit->NotReady */
[21878]144 AutoUninitSpan autoUninitSpan(this);
[13606]145 if (autoUninitSpan.uninitDone())
146 return;
[1]147
[28909]148 for (unsigned i = 0; i < KEYBOARD_MAX_DEVICES; ++i)
149 {
150 if (mpDrv[i])
151 mpDrv[i]->pKeyboard = NULL;
152 mpDrv[i] = NULL;
153 }
[13606]154
[52924]155 menmLeds = PDMKEYBLEDS_NONE;
156
[27607]157 unconst(mParent) = NULL;
[33061]158 unconst(mEventSource).setNull();
[1]159}
160
161/**
162 * Sends a scancode to the keyboard.
163 *
164 * @returns COM status code
[52400]165 * @param aScancode The scancode to send
[1]166 */
[52400]167HRESULT Keyboard::putScancode(LONG aScancode)
[1]168{
[52400]169 std::vector<LONG> scancodes;
170 scancodes.resize(1);
171 scancodes[0] = aScancode;
172 return putScancodes(scancodes, NULL);
[1]173}
174
175/**
176 * Sends a list of scancodes to the keyboard.
177 *
178 * @returns COM status code
[52400]179 * @param aScancodes Pointer to the first scancode
180 * @param aCodesStored Address of variable to store the number
181 * of scancodes that were sent to the keyboard.
182 This value can be NULL.
[1]183 */
[52400]184HRESULT Keyboard::putScancodes(const std::vector<LONG> &aScancodes,
185 ULONG *aCodesStored)
[1]186{
[25310]187 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[1]188
[28909]189 CHECK_CONSOLE_DRV(mpDrv[0]);
[1]190
[28909]191 /* Send input to the last enabled device. Relies on the fact that
192 * the USB keyboard is always initialized after the PS/2 keyboard.
193 */
194 PPDMIKEYBOARDPORT pUpPort = NULL;
195 for (int i = KEYBOARD_MAX_DEVICES - 1; i >= 0 ; --i)
196 {
197 if (mpDrv[i] && (mpDrv[i]->u32DevCaps & KEYBOARD_DEVCAP_ENABLED))
198 {
199 pUpPort = mpDrv[i]->pUpPort;
200 break;
201 }
202 }
[52400]203
[28909]204 /* No enabled keyboard - throw the input away. */
205 if (!pUpPort)
[34288]206 {
[52400]207 if (aCodesStored)
208 *aCodesStored = (uint32_t)aScancodes.size();
[34288]209 return S_OK;
210 }
[28909]211
[13606]212 int vrc = VINF_SUCCESS;
[1]213
[34288]214 uint32_t sent;
[52400]215 for (sent = 0; (sent < aScancodes.size()) && RT_SUCCESS(vrc); ++sent)
216 vrc = pUpPort->pfnPutEventScan(pUpPort, (uint8_t)aScancodes[sent]);
[1]217
[52400]218 if (aCodesStored)
219 *aCodesStored = sent;
[34012]220
[52934]221 com::SafeArray<LONG> keys(aScancodes.size());
222 for (size_t i = 0; i < aScancodes.size(); ++i)
223 keys[i] = aScancodes[i];
224
[85300]225 ::FireGuestKeyboardEvent(mEventSource, ComSafeArrayAsInParam(keys));
[34395]226
[34288]227 if (RT_FAILURE(vrc))
[73003]228 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
229 tr("Could not send all scan codes to the virtual keyboard (%Rrc)"),
230 vrc);
[34288]231
232 return S_OK;
[1]233}
234
235/**
[79676]236 * Sends a HID usage code and page to the keyboard.
237 *
238 * @returns COM status code
239 * @param aUsageCode The HID usage code to send
240 * @param aUsagePage The HID usage page corresponding to the code
241 * @param fKeyRelease The key release flag
242 */
243HRESULT Keyboard::putUsageCode(LONG aUsageCode, LONG aUsagePage, BOOL fKeyRelease)
244{
245 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
246
247 CHECK_CONSOLE_DRV(mpDrv[0]);
248
249 /* Send input to the last enabled device. Relies on the fact that
250 * the USB keyboard is always initialized after the PS/2 keyboard.
251 */
252 PPDMIKEYBOARDPORT pUpPort = NULL;
253 for (int i = KEYBOARD_MAX_DEVICES - 1; i >= 0 ; --i)
254 {
255 if (mpDrv[i] && (mpDrv[i]->u32DevCaps & KEYBOARD_DEVCAP_ENABLED))
256 {
257 pUpPort = mpDrv[i]->pUpPort;
258 break;
259 }
260 }
261
262 /* No enabled keyboard - throw the input away. */
263 if (!pUpPort)
264 return S_OK;
265
[82213]266 uint32_t idUsage = (uint16_t)aUsageCode | ((uint32_t)(uint8_t)aUsagePage << 16) | (fKeyRelease ? UINT32_C(0x80000000) : 0);
267 int vrc = pUpPort->pfnPutEventHid(pUpPort, idUsage);
[79676]268 if (RT_FAILURE(vrc))
269 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
270 tr("Could not send usage code to the virtual keyboard (%Rrc)"),
271 vrc);
272
273 return S_OK;
274}
275
276/**
[1]277 * Sends Control-Alt-Delete to the keyboard. This could be done otherwise
278 * but it's so common that we'll be nice and supply a convenience API.
279 *
280 * @returns COM status code
281 *
282 */
[52400]283HRESULT Keyboard::putCAD()
[1]284{
[63147]285 std::vector<LONG> cadSequence;
[52400]286 cadSequence.resize(8);
[1]287
[26186]288 cadSequence[0] = 0x1d; // Ctrl down
289 cadSequence[1] = 0x38; // Alt down
[36724]290 cadSequence[2] = 0xe0; // Del down 1
291 cadSequence[3] = 0x53; // Del down 2
292 cadSequence[4] = 0xe0; // Del up 1
293 cadSequence[5] = 0xd3; // Del up 2
294 cadSequence[6] = 0xb8; // Alt up
295 cadSequence[7] = 0x9d; // Ctrl up
[13606]296
[52400]297 return putScancodes(cadSequence, NULL);
[1]298}
299
[49386]300/**
301 * Releases all currently held keys in the virtual keyboard.
302 *
303 * @returns COM status code
304 *
305 */
[52400]306HRESULT Keyboard::releaseKeys()
[49386]307{
[89935]308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
309
310 /* Release all keys on the active keyboard in order to start with a clean slate.
311 * Note that this should mirror the logic in Keyboard::putScancodes() when choosing
312 * which keyboard to send the release event to.
313 */
314 PPDMIKEYBOARDPORT pUpPort = NULL;
315 for (int i = KEYBOARD_MAX_DEVICES - 1; i >= 0 ; --i)
316 {
317 if (mpDrv[i] && (mpDrv[i]->u32DevCaps & KEYBOARD_DEVCAP_ENABLED))
318 {
319 pUpPort = mpDrv[i]->pUpPort;
320 break;
321 }
322 }
323
324 if (pUpPort)
325 {
[94923]326 int vrc = pUpPort->pfnReleaseKeys(pUpPort);
327 if (RT_FAILURE(vrc))
328 AssertMsgFailed(("Failed to release keys on all keyboards! vrc=%Rrc\n", vrc));
[89935]329 }
330
331 return S_OK;
[49386]332}
333
[52924]334HRESULT Keyboard::getKeyboardLEDs(std::vector<KeyboardLED_T> &aKeyboardLEDs)
335{
336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
[52400]337
[52924]338 aKeyboardLEDs.resize(0);
339
340 if (menmLeds & PDMKEYBLEDS_NUMLOCK) aKeyboardLEDs.push_back(KeyboardLED_NumLock);
341 if (menmLeds & PDMKEYBLEDS_CAPSLOCK) aKeyboardLEDs.push_back(KeyboardLED_CapsLock);
342 if (menmLeds & PDMKEYBLEDS_SCROLLLOCK) aKeyboardLEDs.push_back(KeyboardLED_ScrollLock);
343
344 return S_OK;
345}
346
[52400]347HRESULT Keyboard::getEventSource(ComPtr<IEventSource> &aEventSource)
[33061]348{
[52400]349 // No need to lock - lifetime constant
350 mEventSource.queryInterfaceTo(aEventSource.asOutParam());
[33061]351
352 return S_OK;
353}
354
[1]355//
356// private methods
357//
[52924]358void Keyboard::onKeyboardLedsChange(PDMKEYBLEDS enmLeds)
359{
360 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
361
362 /* Save the current status. */
363 menmLeds = enmLeds;
364
365 alock.release();
366
367 i_getParent()->i_onKeyboardLedsChange(RT_BOOL(enmLeds & PDMKEYBLEDS_NUMLOCK),
368 RT_BOOL(enmLeds & PDMKEYBLEDS_CAPSLOCK),
369 RT_BOOL(enmLeds & PDMKEYBLEDS_SCROLLLOCK));
370}
371
[52400]372DECLCALLBACK(void) Keyboard::i_keyboardLedStatusChange(PPDMIKEYBOARDCONNECTOR pInterface, PDMKEYBLEDS enmLeds)
[45029]373{
[51096]374 PDRVMAINKEYBOARD pDrv = RT_FROM_MEMBER(pInterface, DRVMAINKEYBOARD, IConnector);
[52924]375 pDrv->pKeyboard->onKeyboardLedsChange(enmLeds);
[45029]376}
377
[1]378/**
[45029]379 * @interface_method_impl{PDMIKEYBOARDCONNECTOR,pfnSetActive}
380 */
[52400]381DECLCALLBACK(void) Keyboard::i_keyboardSetActive(PPDMIKEYBOARDCONNECTOR pInterface, bool fActive)
[45029]382{
[51096]383 PDRVMAINKEYBOARD pDrv = RT_FROM_MEMBER(pInterface, DRVMAINKEYBOARD, IConnector);
[89935]384
385 // Before activating a different keyboard, release all keys on the currently active one.
[45029]386 if (fActive)
[89935]387 pDrv->pKeyboard->releaseKeys();
388
389 if (fActive)
[45029]390 pDrv->u32DevCaps |= KEYBOARD_DEVCAP_ENABLED;
391 else
392 pDrv->u32DevCaps &= ~KEYBOARD_DEVCAP_ENABLED;
393}
394
395
396/**
[25966]397 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
[1]398 */
[52400]399DECLCALLBACK(void *) Keyboard::i_drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
[1]400{
[25966]401 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
402 PDRVMAINKEYBOARD pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
[25985]403
404 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
405 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIKEYBOARDCONNECTOR, &pDrv->IConnector);
[25966]406 return NULL;
[1]407}
408
409
410/**
411 * Destruct a keyboard driver instance.
412 *
413 * @param pDrvIns The driver instance data.
414 */
[52400]415DECLCALLBACK(void) Keyboard::i_drvDestruct(PPDMDRVINS pDrvIns)
[1]416{
[45029]417 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
[45030]418 PDRVMAINKEYBOARD pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
[1]419 LogFlow(("Keyboard::drvDestruct: iInstance=%d\n", pDrvIns->iInstance));
[26001]420
[45030]421 if (pThis->pKeyboard)
[1]422 {
[45030]423 AutoWriteLock kbdLock(pThis->pKeyboard COMMA_LOCKVAL_SRC_POS);
[28909]424 for (unsigned cDev = 0; cDev < KEYBOARD_MAX_DEVICES; ++cDev)
[45030]425 if (pThis->pKeyboard->mpDrv[cDev] == pThis)
[28909]426 {
[45030]427 pThis->pKeyboard->mpDrv[cDev] = NULL;
[28909]428 break;
429 }
[1]430 }
431}
432
433/**
434 * Construct a keyboard driver instance.
435 *
[22277]436 * @copydoc FNPDMDRVCONSTRUCT
[1]437 */
[52400]438DECLCALLBACK(int) Keyboard::i_drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
[1]439{
[45029]440 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
[93444]441 RT_NOREF(fFlags, pCfg);
[45030]442 PDRVMAINKEYBOARD pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
[1]443 LogFlow(("Keyboard::drvConstruct: iInstance=%d\n", pDrvIns->iInstance));
444
445 /*
446 * Validate configuration.
447 */
[93444]448 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "", "");
[25149]449 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
[22277]450 ("Configuration error: Not possible to attach anything to this driver!\n"),
451 VERR_PDM_DRVINS_NO_ATTACH);
[1]452
453 /*
454 * IBase.
455 */
[52400]456 pDrvIns->IBase.pfnQueryInterface = Keyboard::i_drvQueryInterface;
[1]457
[52400]458 pThis->IConnector.pfnLedStatusChange = i_keyboardLedStatusChange;
459 pThis->IConnector.pfnSetActive = Keyboard::i_keyboardSetActive;
[1]460
461 /*
462 * Get the IKeyboardPort interface of the above driver/device.
463 */
[45030]464 pThis->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIKEYBOARDPORT);
465 if (!pThis->pUpPort)
[1]466 {
[32718]467 AssertMsgFailed(("Configuration error: No keyboard port interface above!\n"));
[1]468 return VERR_PDM_MISSING_INTERFACE_ABOVE;
469 }
470
471 /*
472 * Get the Keyboard object pointer and update the mpDrv member.
473 */
[89951]474 com::Guid uuid(COM_IIDOF(IKeyboard));
475 IKeyboard *pIKeyboard = (IKeyboard *)PDMDrvHlpQueryGenericUserObject(pDrvIns, uuid.raw());
476 if (!pIKeyboard)
[1]477 {
[89951]478 AssertMsgFailed(("Configuration error: No/bad Keyboard object!\n"));
479 return VERR_NOT_FOUND;
[1]480 }
[89951]481 pThis->pKeyboard = static_cast<Keyboard *>(pIKeyboard);
482
[28909]483 unsigned cDev;
484 for (cDev = 0; cDev < KEYBOARD_MAX_DEVICES; ++cDev)
[45030]485 if (!pThis->pKeyboard->mpDrv[cDev])
[28909]486 {
[45030]487 pThis->pKeyboard->mpDrv[cDev] = pThis;
[28909]488 break;
489 }
490 if (cDev == KEYBOARD_MAX_DEVICES)
491 return VERR_NO_MORE_HANDLES;
[1]492
493 return VINF_SUCCESS;
494}
495
496
497/**
498 * Keyboard driver registration record.
499 */
500const PDMDRVREG Keyboard::DrvReg =
501{
502 /* u32Version */
503 PDM_DRVREG_VERSION,
[26166]504 /* szName */
[1]505 "MainKeyboard",
[25893]506 /* szRCMod */
507 "",
508 /* szR0Mod */
509 "",
[1]510 /* pszDescription */
511 "Main keyboard driver (Main as in the API).",
512 /* fFlags */
513 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
514 /* fClass. */
515 PDM_DRVREG_CLASS_KEYBOARD,
516 /* cMaxInstances */
[40282]517 ~0U,
[1]518 /* cbInstance */
519 sizeof(DRVMAINKEYBOARD),
520 /* pfnConstruct */
[52400]521 Keyboard::i_drvConstruct,
[1]522 /* pfnDestruct */
[52400]523 Keyboard::i_drvDestruct,
[25893]524 /* pfnRelocate */
525 NULL,
[1]526 /* pfnIOCtl */
527 NULL,
528 /* pfnPowerOn */
529 NULL,
530 /* pfnReset */
531 NULL,
532 /* pfnSuspend */
533 NULL,
534 /* pfnResume */
535 NULL,
[22277]536 /* pfnAttach */
537 NULL,
[1]538 /* pfnDetach */
[25149]539 NULL,
[22277]540 /* pfnPowerOff */
[25149]541 NULL,
[22277]542 /* pfnSoftReset */
543 NULL,
544 /* u32EndVersion */
545 PDM_DRVREG_VERSION
[1]546};
[14772]547/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use