VirtualBox

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

Last change on this file since 75142 was 73003, checked in by vboxsync, 6 years ago

Main: Use setErrorBoth when we've got a VBox status code handy. (The COM status codes aren't too specfic and this may help us decode error messages and provide an alternative to strstr for API clients. setErrorBoth isn't new, btw.)

  • 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 73003 2018-07-09 11:09:32Z 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 setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
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.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette