VirtualBox

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

Last change on this file since 70772 was 69500, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use