VirtualBox

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

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

Copyright year updates by scm.

  • 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 82968 2020-02-04 10:35:17Z 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
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 uint32_t idUsage = (uint16_t)aUsageCode | ((uint32_t)(uint8_t)aUsagePage << 16) | (fKeyRelease ? UINT32_C(0x80000000) : 0);
258 int vrc = pUpPort->pfnPutEventHid(pUpPort, idUsage);
259 if (RT_FAILURE(vrc))
260 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
261 tr("Could not send usage code to the virtual keyboard (%Rrc)"),
262 vrc);
263
264 return S_OK;
265}
266
267/**
268 * Sends Control-Alt-Delete to the keyboard. This could be done otherwise
269 * but it's so common that we'll be nice and supply a convenience API.
270 *
271 * @returns COM status code
272 *
273 */
274HRESULT Keyboard::putCAD()
275{
276 std::vector<LONG> cadSequence;
277 cadSequence.resize(8);
278
279 cadSequence[0] = 0x1d; // Ctrl down
280 cadSequence[1] = 0x38; // Alt down
281 cadSequence[2] = 0xe0; // Del down 1
282 cadSequence[3] = 0x53; // Del down 2
283 cadSequence[4] = 0xe0; // Del up 1
284 cadSequence[5] = 0xd3; // Del up 2
285 cadSequence[6] = 0xb8; // Alt up
286 cadSequence[7] = 0x9d; // Ctrl up
287
288 return putScancodes(cadSequence, NULL);
289}
290
291/**
292 * Releases all currently held keys in the virtual keyboard.
293 *
294 * @returns COM status code
295 *
296 */
297HRESULT Keyboard::releaseKeys()
298{
299 std::vector<LONG> scancodes;
300 scancodes.resize(1);
301 scancodes[0] = 0xFC; /* Magic scancode, see PS/2 and USB keyboard devices. */
302 return putScancodes(scancodes, NULL);
303}
304
305HRESULT Keyboard::getKeyboardLEDs(std::vector<KeyboardLED_T> &aKeyboardLEDs)
306{
307 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
308
309 aKeyboardLEDs.resize(0);
310
311 if (menmLeds & PDMKEYBLEDS_NUMLOCK) aKeyboardLEDs.push_back(KeyboardLED_NumLock);
312 if (menmLeds & PDMKEYBLEDS_CAPSLOCK) aKeyboardLEDs.push_back(KeyboardLED_CapsLock);
313 if (menmLeds & PDMKEYBLEDS_SCROLLLOCK) aKeyboardLEDs.push_back(KeyboardLED_ScrollLock);
314
315 return S_OK;
316}
317
318HRESULT Keyboard::getEventSource(ComPtr<IEventSource> &aEventSource)
319{
320 // No need to lock - lifetime constant
321 mEventSource.queryInterfaceTo(aEventSource.asOutParam());
322
323 return S_OK;
324}
325
326//
327// private methods
328//
329void Keyboard::onKeyboardLedsChange(PDMKEYBLEDS enmLeds)
330{
331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
332
333 /* Save the current status. */
334 menmLeds = enmLeds;
335
336 alock.release();
337
338 i_getParent()->i_onKeyboardLedsChange(RT_BOOL(enmLeds & PDMKEYBLEDS_NUMLOCK),
339 RT_BOOL(enmLeds & PDMKEYBLEDS_CAPSLOCK),
340 RT_BOOL(enmLeds & PDMKEYBLEDS_SCROLLLOCK));
341}
342
343DECLCALLBACK(void) Keyboard::i_keyboardLedStatusChange(PPDMIKEYBOARDCONNECTOR pInterface, PDMKEYBLEDS enmLeds)
344{
345 PDRVMAINKEYBOARD pDrv = RT_FROM_MEMBER(pInterface, DRVMAINKEYBOARD, IConnector);
346 pDrv->pKeyboard->onKeyboardLedsChange(enmLeds);
347}
348
349/**
350 * @interface_method_impl{PDMIKEYBOARDCONNECTOR,pfnSetActive}
351 */
352DECLCALLBACK(void) Keyboard::i_keyboardSetActive(PPDMIKEYBOARDCONNECTOR pInterface, bool fActive)
353{
354 PDRVMAINKEYBOARD pDrv = RT_FROM_MEMBER(pInterface, DRVMAINKEYBOARD, IConnector);
355 if (fActive)
356 pDrv->u32DevCaps |= KEYBOARD_DEVCAP_ENABLED;
357 else
358 pDrv->u32DevCaps &= ~KEYBOARD_DEVCAP_ENABLED;
359}
360
361
362/**
363 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
364 */
365DECLCALLBACK(void *) Keyboard::i_drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
366{
367 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
368 PDRVMAINKEYBOARD pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
369
370 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
371 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIKEYBOARDCONNECTOR, &pDrv->IConnector);
372 return NULL;
373}
374
375
376/**
377 * Destruct a keyboard driver instance.
378 *
379 * @returns VBox status code.
380 * @param pDrvIns The driver instance data.
381 */
382DECLCALLBACK(void) Keyboard::i_drvDestruct(PPDMDRVINS pDrvIns)
383{
384 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
385 PDRVMAINKEYBOARD pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
386 LogFlow(("Keyboard::drvDestruct: iInstance=%d\n", pDrvIns->iInstance));
387
388 if (pThis->pKeyboard)
389 {
390 AutoWriteLock kbdLock(pThis->pKeyboard COMMA_LOCKVAL_SRC_POS);
391 for (unsigned cDev = 0; cDev < KEYBOARD_MAX_DEVICES; ++cDev)
392 if (pThis->pKeyboard->mpDrv[cDev] == pThis)
393 {
394 pThis->pKeyboard->mpDrv[cDev] = NULL;
395 break;
396 }
397 }
398}
399
400/**
401 * Construct a keyboard driver instance.
402 *
403 * @copydoc FNPDMDRVCONSTRUCT
404 */
405DECLCALLBACK(int) Keyboard::i_drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
406{
407 RT_NOREF(fFlags);
408 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
409 PDRVMAINKEYBOARD pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
410 LogFlow(("Keyboard::drvConstruct: iInstance=%d\n", pDrvIns->iInstance));
411
412 /*
413 * Validate configuration.
414 */
415 if (!CFGMR3AreValuesValid(pCfg, "Object\0"))
416 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
417 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
418 ("Configuration error: Not possible to attach anything to this driver!\n"),
419 VERR_PDM_DRVINS_NO_ATTACH);
420
421 /*
422 * IBase.
423 */
424 pDrvIns->IBase.pfnQueryInterface = Keyboard::i_drvQueryInterface;
425
426 pThis->IConnector.pfnLedStatusChange = i_keyboardLedStatusChange;
427 pThis->IConnector.pfnSetActive = Keyboard::i_keyboardSetActive;
428
429 /*
430 * Get the IKeyboardPort interface of the above driver/device.
431 */
432 pThis->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIKEYBOARDPORT);
433 if (!pThis->pUpPort)
434 {
435 AssertMsgFailed(("Configuration error: No keyboard port interface above!\n"));
436 return VERR_PDM_MISSING_INTERFACE_ABOVE;
437 }
438
439 /*
440 * Get the Keyboard object pointer and update the mpDrv member.
441 */
442 void *pv;
443 int rc = CFGMR3QueryPtr(pCfg, "Object", &pv);
444 if (RT_FAILURE(rc))
445 {
446 AssertMsgFailed(("Configuration error: No/bad \"Object\" value! rc=%Rrc\n", rc));
447 return rc;
448 }
449 pThis->pKeyboard = (Keyboard *)pv; /** @todo Check this cast! */
450 unsigned cDev;
451 for (cDev = 0; cDev < KEYBOARD_MAX_DEVICES; ++cDev)
452 if (!pThis->pKeyboard->mpDrv[cDev])
453 {
454 pThis->pKeyboard->mpDrv[cDev] = pThis;
455 break;
456 }
457 if (cDev == KEYBOARD_MAX_DEVICES)
458 return VERR_NO_MORE_HANDLES;
459
460 return VINF_SUCCESS;
461}
462
463
464/**
465 * Keyboard driver registration record.
466 */
467const PDMDRVREG Keyboard::DrvReg =
468{
469 /* u32Version */
470 PDM_DRVREG_VERSION,
471 /* szName */
472 "MainKeyboard",
473 /* szRCMod */
474 "",
475 /* szR0Mod */
476 "",
477 /* pszDescription */
478 "Main keyboard driver (Main as in the API).",
479 /* fFlags */
480 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
481 /* fClass. */
482 PDM_DRVREG_CLASS_KEYBOARD,
483 /* cMaxInstances */
484 ~0U,
485 /* cbInstance */
486 sizeof(DRVMAINKEYBOARD),
487 /* pfnConstruct */
488 Keyboard::i_drvConstruct,
489 /* pfnDestruct */
490 Keyboard::i_drvDestruct,
491 /* pfnRelocate */
492 NULL,
493 /* pfnIOCtl */
494 NULL,
495 /* pfnPowerOn */
496 NULL,
497 /* pfnReset */
498 NULL,
499 /* pfnSuspend */
500 NULL,
501 /* pfnResume */
502 NULL,
503 /* pfnAttach */
504 NULL,
505 /* pfnDetach */
506 NULL,
507 /* pfnPowerOff */
508 NULL,
509 /* pfnSoftReset */
510 NULL,
511 /* u32EndVersion */
512 PDM_DRVREG_VERSION
513};
514/* 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