VirtualBox

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

Last change on this file since 107495 was 107495, checked in by vboxsync, 2 weeks ago

src/VBox/Main/src-client/KeyboardImpl.cpp: Fixed a warning found by Parfait. jiraref:VBP-1424

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