VirtualBox

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

Last change on this file since 68808 was 67914, checked in by vboxsync, 8 years ago

src-client: Define LOG_GROUP according to interface or similar.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.6 KB
Line 
1/* $Id: KeyboardImpl.cpp 67914 2017-07-11 20:46:37Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2016 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 mpVMMDev = NULL;
82 mfVMMDevInited = false;
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 mpVMMDev = NULL;
145 mfVMMDevInited = true;
146
147 menmLeds = PDMKEYBLEDS_NONE;
148
149 unconst(mParent) = NULL;
150 unconst(mEventSource).setNull();
151}
152
153/**
154 * Sends a scancode to the keyboard.
155 *
156 * @returns COM status code
157 * @param aScancode The scancode to send
158 */
159HRESULT Keyboard::putScancode(LONG aScancode)
160{
161 std::vector<LONG> scancodes;
162 scancodes.resize(1);
163 scancodes[0] = aScancode;
164 return putScancodes(scancodes, NULL);
165}
166
167/**
168 * Sends a list of scancodes to the keyboard.
169 *
170 * @returns COM status code
171 * @param aScancodes Pointer to the first scancode
172 * @param aCodesStored Address of variable to store the number
173 * of scancodes that were sent to the keyboard.
174 This value can be NULL.
175 */
176HRESULT Keyboard::putScancodes(const std::vector<LONG> &aScancodes,
177 ULONG *aCodesStored)
178{
179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
180
181 CHECK_CONSOLE_DRV(mpDrv[0]);
182
183 /* Send input to the last enabled device. Relies on the fact that
184 * the USB keyboard is always initialized after the PS/2 keyboard.
185 */
186 PPDMIKEYBOARDPORT pUpPort = NULL;
187 for (int i = KEYBOARD_MAX_DEVICES - 1; i >= 0 ; --i)
188 {
189 if (mpDrv[i] && (mpDrv[i]->u32DevCaps & KEYBOARD_DEVCAP_ENABLED))
190 {
191 pUpPort = mpDrv[i]->pUpPort;
192 break;
193 }
194 }
195
196 /* No enabled keyboard - throw the input away. */
197 if (!pUpPort)
198 {
199 if (aCodesStored)
200 *aCodesStored = (uint32_t)aScancodes.size();
201 return S_OK;
202 }
203
204 int vrc = VINF_SUCCESS;
205
206 uint32_t sent;
207 for (sent = 0; (sent < aScancodes.size()) && RT_SUCCESS(vrc); ++sent)
208 vrc = pUpPort->pfnPutEventScan(pUpPort, (uint8_t)aScancodes[sent]);
209
210 if (aCodesStored)
211 *aCodesStored = sent;
212
213 com::SafeArray<LONG> keys(aScancodes.size());
214 for (size_t i = 0; i < aScancodes.size(); ++i)
215 keys[i] = aScancodes[i];
216
217 VBoxEventDesc evDesc;
218 evDesc.init(mEventSource, VBoxEventType_OnGuestKeyboard, ComSafeArrayAsInParam(keys));
219 evDesc.fire(0);
220
221 if (RT_FAILURE(vrc))
222 return setError(VBOX_E_IPRT_ERROR,
223 tr("Could not send all scan codes to the virtual keyboard (%Rrc)"),
224 vrc);
225
226 return S_OK;
227}
228
229/**
230 * Sends Control-Alt-Delete to the keyboard. This could be done otherwise
231 * but it's so common that we'll be nice and supply a convenience API.
232 *
233 * @returns COM status code
234 *
235 */
236HRESULT Keyboard::putCAD()
237{
238 std::vector<LONG> cadSequence;
239 cadSequence.resize(8);
240
241 cadSequence[0] = 0x1d; // Ctrl down
242 cadSequence[1] = 0x38; // Alt down
243 cadSequence[2] = 0xe0; // Del down 1
244 cadSequence[3] = 0x53; // Del down 2
245 cadSequence[4] = 0xe0; // Del up 1
246 cadSequence[5] = 0xd3; // Del up 2
247 cadSequence[6] = 0xb8; // Alt up
248 cadSequence[7] = 0x9d; // Ctrl up
249
250 return putScancodes(cadSequence, NULL);
251}
252
253/**
254 * Releases all currently held keys in the virtual keyboard.
255 *
256 * @returns COM status code
257 *
258 */
259HRESULT Keyboard::releaseKeys()
260{
261 std::vector<LONG> scancodes;
262 scancodes.resize(1);
263 scancodes[0] = 0xFC; /* Magic scancode, see PS/2 and USB keyboard devices. */
264 return putScancodes(scancodes, NULL);
265}
266
267HRESULT Keyboard::getKeyboardLEDs(std::vector<KeyboardLED_T> &aKeyboardLEDs)
268{
269 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
270
271 aKeyboardLEDs.resize(0);
272
273 if (menmLeds & PDMKEYBLEDS_NUMLOCK) aKeyboardLEDs.push_back(KeyboardLED_NumLock);
274 if (menmLeds & PDMKEYBLEDS_CAPSLOCK) aKeyboardLEDs.push_back(KeyboardLED_CapsLock);
275 if (menmLeds & PDMKEYBLEDS_SCROLLLOCK) aKeyboardLEDs.push_back(KeyboardLED_ScrollLock);
276
277 return S_OK;
278}
279
280HRESULT Keyboard::getEventSource(ComPtr<IEventSource> &aEventSource)
281{
282 // No need to lock - lifetime constant
283 mEventSource.queryInterfaceTo(aEventSource.asOutParam());
284
285 return S_OK;
286}
287
288//
289// private methods
290//
291void Keyboard::onKeyboardLedsChange(PDMKEYBLEDS enmLeds)
292{
293 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
294
295 /* Save the current status. */
296 menmLeds = enmLeds;
297
298 alock.release();
299
300 i_getParent()->i_onKeyboardLedsChange(RT_BOOL(enmLeds & PDMKEYBLEDS_NUMLOCK),
301 RT_BOOL(enmLeds & PDMKEYBLEDS_CAPSLOCK),
302 RT_BOOL(enmLeds & PDMKEYBLEDS_SCROLLLOCK));
303}
304
305DECLCALLBACK(void) Keyboard::i_keyboardLedStatusChange(PPDMIKEYBOARDCONNECTOR pInterface, PDMKEYBLEDS enmLeds)
306{
307 PDRVMAINKEYBOARD pDrv = RT_FROM_MEMBER(pInterface, DRVMAINKEYBOARD, IConnector);
308 pDrv->pKeyboard->onKeyboardLedsChange(enmLeds);
309}
310
311/**
312 * @interface_method_impl{PDMIKEYBOARDCONNECTOR,pfnSetActive}
313 */
314DECLCALLBACK(void) Keyboard::i_keyboardSetActive(PPDMIKEYBOARDCONNECTOR pInterface, bool fActive)
315{
316 PDRVMAINKEYBOARD pDrv = RT_FROM_MEMBER(pInterface, DRVMAINKEYBOARD, IConnector);
317 if (fActive)
318 pDrv->u32DevCaps |= KEYBOARD_DEVCAP_ENABLED;
319 else
320 pDrv->u32DevCaps &= ~KEYBOARD_DEVCAP_ENABLED;
321}
322
323
324/**
325 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
326 */
327DECLCALLBACK(void *) Keyboard::i_drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
328{
329 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
330 PDRVMAINKEYBOARD pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
331
332 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
333 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIKEYBOARDCONNECTOR, &pDrv->IConnector);
334 return NULL;
335}
336
337
338/**
339 * Destruct a keyboard driver instance.
340 *
341 * @returns VBox status code.
342 * @param pDrvIns The driver instance data.
343 */
344DECLCALLBACK(void) Keyboard::i_drvDestruct(PPDMDRVINS pDrvIns)
345{
346 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
347 PDRVMAINKEYBOARD pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
348 LogFlow(("Keyboard::drvDestruct: iInstance=%d\n", pDrvIns->iInstance));
349
350 if (pThis->pKeyboard)
351 {
352 AutoWriteLock kbdLock(pThis->pKeyboard COMMA_LOCKVAL_SRC_POS);
353 for (unsigned cDev = 0; cDev < KEYBOARD_MAX_DEVICES; ++cDev)
354 if (pThis->pKeyboard->mpDrv[cDev] == pThis)
355 {
356 pThis->pKeyboard->mpDrv[cDev] = NULL;
357 break;
358 }
359 pThis->pKeyboard->mpVMMDev = NULL;
360 }
361}
362
363/**
364 * Construct a keyboard driver instance.
365 *
366 * @copydoc FNPDMDRVCONSTRUCT
367 */
368DECLCALLBACK(int) Keyboard::i_drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
369{
370 RT_NOREF(fFlags);
371 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
372 PDRVMAINKEYBOARD pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
373 LogFlow(("Keyboard::drvConstruct: iInstance=%d\n", pDrvIns->iInstance));
374
375 /*
376 * Validate configuration.
377 */
378 if (!CFGMR3AreValuesValid(pCfg, "Object\0"))
379 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
380 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
381 ("Configuration error: Not possible to attach anything to this driver!\n"),
382 VERR_PDM_DRVINS_NO_ATTACH);
383
384 /*
385 * IBase.
386 */
387 pDrvIns->IBase.pfnQueryInterface = Keyboard::i_drvQueryInterface;
388
389 pThis->IConnector.pfnLedStatusChange = i_keyboardLedStatusChange;
390 pThis->IConnector.pfnSetActive = Keyboard::i_keyboardSetActive;
391
392 /*
393 * Get the IKeyboardPort interface of the above driver/device.
394 */
395 pThis->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIKEYBOARDPORT);
396 if (!pThis->pUpPort)
397 {
398 AssertMsgFailed(("Configuration error: No keyboard port interface above!\n"));
399 return VERR_PDM_MISSING_INTERFACE_ABOVE;
400 }
401
402 /*
403 * Get the Keyboard object pointer and update the mpDrv member.
404 */
405 void *pv;
406 int rc = CFGMR3QueryPtr(pCfg, "Object", &pv);
407 if (RT_FAILURE(rc))
408 {
409 AssertMsgFailed(("Configuration error: No/bad \"Object\" value! rc=%Rrc\n", rc));
410 return rc;
411 }
412 pThis->pKeyboard = (Keyboard *)pv; /** @todo Check this cast! */
413 unsigned cDev;
414 for (cDev = 0; cDev < KEYBOARD_MAX_DEVICES; ++cDev)
415 if (!pThis->pKeyboard->mpDrv[cDev])
416 {
417 pThis->pKeyboard->mpDrv[cDev] = pThis;
418 break;
419 }
420 if (cDev == KEYBOARD_MAX_DEVICES)
421 return VERR_NO_MORE_HANDLES;
422
423 return VINF_SUCCESS;
424}
425
426
427/**
428 * Keyboard driver registration record.
429 */
430const PDMDRVREG Keyboard::DrvReg =
431{
432 /* u32Version */
433 PDM_DRVREG_VERSION,
434 /* szName */
435 "MainKeyboard",
436 /* szRCMod */
437 "",
438 /* szR0Mod */
439 "",
440 /* pszDescription */
441 "Main keyboard driver (Main as in the API).",
442 /* fFlags */
443 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
444 /* fClass. */
445 PDM_DRVREG_CLASS_KEYBOARD,
446 /* cMaxInstances */
447 ~0U,
448 /* cbInstance */
449 sizeof(DRVMAINKEYBOARD),
450 /* pfnConstruct */
451 Keyboard::i_drvConstruct,
452 /* pfnDestruct */
453 Keyboard::i_drvDestruct,
454 /* pfnRelocate */
455 NULL,
456 /* pfnIOCtl */
457 NULL,
458 /* pfnPowerOn */
459 NULL,
460 /* pfnReset */
461 NULL,
462 /* pfnSuspend */
463 NULL,
464 /* pfnResume */
465 NULL,
466 /* pfnAttach */
467 NULL,
468 /* pfnDetach */
469 NULL,
470 /* pfnPowerOff */
471 NULL,
472 /* pfnSoftReset */
473 NULL,
474 /* u32EndVersion */
475 PDM_DRVREG_VERSION
476};
477/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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