VirtualBox

source: vbox/trunk/src/VBox/Main/KeyboardImpl.cpp@ 34079

Last change on this file since 34079 was 34012, checked in by vboxsync, 14 years ago

Main: keyboard events now work with RDP

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