/***************************************************************************** * * DIGenK.c * * Copyright (c) 1996 Microsoft Corporation. All Rights Reserved. * * Abstract: * * Generic IDirectInputDevice callback for keyboard. * * Contents: * * CKbd_CreateInstance * *****************************************************************************/ #include "dinputpr.h" /***************************************************************************** * * Some holes in windows.h on NT platforms. * *****************************************************************************/ #ifndef VK_KANA #define VK_KANA 0x15 #endif /***************************************************************************** * * The sqiffle for this file. * *****************************************************************************/ #define sqfl sqflKbd /***************************************************************************** * * Declare the interfaces we will be providing. * * WARNING! If you add a secondary interface, you must also change * CKbd_New! * *****************************************************************************/ Primary_Interface(CKbd, IDirectInputDeviceCallback); /***************************************************************************** * * @doc INTERNAL * * @struct KBDSTAT | * * Internal instantaneous keyboard status information. * * @field BYTE | rgb[DIKBD_CKEYS] | * * Array of key states, one for each logical key. * *****************************************************************************/ typedef struct KBDSTAT { BYTE rgb[DIKBD_CKEYS]; } KBDSTAT, *PKBDSTAT; /***************************************************************************** * * @doc EXTERNAL * * @topic Special remarks on keyboard scan codes | * * There are several aspects of keyboards which applications should * be aware of. Applications are encouraged to allow users to * reconfigure keyboard action keys to suit the physical keyboard * layout. * * For the purposes of this discussion, the baseline keyboard * shall be the US PC Enhanced keyboard. When a key is described * as "missing", it means that the key is present on the US PC * Enhanced keyboard but not on the keyboard under discussion. * When a key is described as "added", it means that the key is * absent on the US PC Enhanced keyboard but present on the * keyboard under discussion. * * Not all PC Enhanced keyboards support the new Windows keys * (DIK_LWIN, DIK_RWIN, and DIK_APPS). There is no way to * determine whether the keys are physically available. * * Note that there is no DIK_PAUSE key code. The PC Enhanced * keyboard does not generate a separate DIK_PAUSE scan code; * rather, it synthesizes a "Pause" from the DIK_LCONTROL and * DIK_NUMLOCK scan codes. * * Keyboards for laptops or other reduced-footprint computers * frequently do not implement a full set of keys. Instead, * some keys (typically numeric keypad keys) are multiplexed * with other keys, selected by an auxiliary "mode" key which * does not generate a separate scan code. * * If the keyboard subtype indicates a PC XT or PC AT keyboard, * then the following keys are not available: * DIK_F11, DIK_F12, and all the extended keys (DIK_* values * greater than or equal to 0x80). Furthermore, the PC XT * keyboard lacks DIK_SYSRQ. * * Japanese keyboards contain a substantially different set of * keys from US keyboards. The following keyboard scan codes * are not available on Japanese keyboards: * DIK_EQUALS, DIK_APOSTROPHE, DIK_GRAVE, DIK_NUMPADENTER, * DIK_RCONTROL, DIK_RMENU. Furthermore, most Japanese * keyboards do not support DIK_RSHIFT. (It is customary * to use DIK_NUMPADEQUAL in place of DIK_RSHIFT.) * * Japanese keyboards contain the following additional keys: * DIK_F14, DIK_NUMPADEQUAL, DIK_CIRCUMFLEX, DIK_AT, DIK_COLON, * DIK_UNDERLINE, DIK_XFER, DIK_NFER, DIK_STOP, DIK_KANA, and * DIK_NUMPADCOMMA. * * Note that on Japanese keyboards, the DIK_CAPSLOCK and * DIK_KANA keys are toggle buttons and not push buttons. * They generate a down event * when first pressed, then generate an up event when pressed a * second time. * Note that on Windows 2000, the DIK_KANJI key is also treated as a * toggle. * *****************************************************************************/ /***************************************************************************** * * @doc INTERNAL * * @global KBDTYPE | c_rgktWhich[] | * * Array that describes which keyboards support which keys. * * The list is optimistic. If any keyboard of the indicated * type supports the key, then we list it. * * Items marks "available for NEC" are keys which are extremely * unlikely to be used in future versions of the Enhanced * keyboard and therefore can be used as ersatz scan codes for * NEC-only keys. * * Note: Kana and CAPSLOCK are toggle buttons on NEC keyboards. * Note: Kana, Kanji and CAPSLOCK are toggle buttons on all NT JPN * keyboards. * *****************************************************************************/ typedef BYTE KBDTYPE; #define KBDTYPE_XT 0x01 /* Key exists on XT class keyboard */ #define KBDTYPE_AT 0x02 /* Key exists on AT class keyboard */ #define KBDTYPE_ENH 0x04 /* Key exists on Enhanced keyboard */ #define KBDTYPE_NEC 0x08 /* Key exists on NEC keyboard */ #define KBDTYPE_ANYKBD 0x0F /* Key exists somewhere in the world */ #define KBDTYPE_NECTGL 0x10 /* Is a toggle-key on NEC keyboard */ #define KBDTYPE_NTTGL 0x20 /* Is a toggle-key on an NT FE keyboard */ #pragma BEGIN_CONST_DATA #define XT KBDTYPE_XT | #define AT KBDTYPE_XT | #define ENH KBDTYPE_ENH | #define NEC KBDTYPE_NEC | #define NECTGL KBDTYPE_NECTGL | #define NTTGL KBDTYPE_NTTGL | KBDTYPE c_rgktWhich[] = { 0, /* 0x00 - */ XT AT ENH NEC 0, /* 0x01 - Esc */ XT AT ENH NEC 0, /* 0x02 - 1 */ XT AT ENH NEC 0, /* 0x03 - 2 */ XT AT ENH NEC 0, /* 0x04 - 3 */ XT AT ENH NEC 0, /* 0x05 - 4 */ XT AT ENH NEC 0, /* 0x06 - 5 */ XT AT ENH NEC 0, /* 0x07 - 6 */ XT AT ENH NEC 0, /* 0x08 - 7 */ XT AT ENH NEC 0, /* 0x09 - 8 */ XT AT ENH NEC 0, /* 0x0A - 9 */ XT AT ENH NEC 0, /* 0x0B - 0 */ XT AT ENH NEC 0, /* 0x0C - - */ XT AT ENH 0, /* 0x0D - = */ XT AT ENH NEC 0, /* 0x0E - BkSp */ XT AT ENH NEC 0, /* 0x0F - Tab */ XT AT ENH NEC 0, /* 0x10 - Q */ XT AT ENH NEC 0, /* 0x11 - W */ XT AT ENH NEC 0, /* 0x12 - E */ XT AT ENH NEC 0, /* 0x13 - R */ XT AT ENH NEC 0, /* 0x14 - T */ XT AT ENH NEC 0, /* 0x15 - Y */ XT AT ENH NEC 0, /* 0x16 - U */ XT AT ENH NEC 0, /* 0x17 - I */ XT AT ENH NEC 0, /* 0x18 - O */ XT AT ENH NEC 0, /* 0x19 - P */ XT AT ENH NEC 0, /* 0x1A - [ */ XT AT ENH NEC 0, /* 0x1B - ] */ XT AT ENH NEC 0, /* 0x1C - Enter */ XT AT ENH NEC 0, /* 0x1D - LCtrl */ XT AT ENH NEC 0, /* 0x1E - A */ XT AT ENH NEC 0, /* 0x1F - S */ XT AT ENH NEC 0, /* 0x20 - D */ XT AT ENH NEC 0, /* 0x21 - F */ XT AT ENH NEC 0, /* 0x22 - G */ XT AT ENH NEC 0, /* 0x23 - H */ XT AT ENH NEC 0, /* 0x24 - J */ XT AT ENH NEC 0, /* 0x25 - K */ XT AT ENH NEC 0, /* 0x26 - L */ XT AT ENH NEC 0, /* 0x27 - ; */ XT AT ENH 0, /* 0x28 - ' */ XT AT ENH 0, /* 0x29 - ` */ XT AT ENH NEC 0, /* 0x2A - LShift */ XT AT ENH NEC 0, /* 0x2B - \ */ XT AT ENH NEC 0, /* 0x2C - Z */ XT AT ENH NEC 0, /* 0x2D - X */ XT AT ENH NEC 0, /* 0x2E - C */ XT AT ENH NEC 0, /* 0x2F - V */ XT AT ENH NEC 0, /* 0x30 - B */ XT AT ENH NEC 0, /* 0x31 - N */ XT AT ENH NEC 0, /* 0x32 - M */ XT AT ENH NEC 0, /* 0x33 - , */ XT AT ENH NEC 0, /* 0x34 - . */ XT AT ENH NEC 0, /* 0x35 - / */ XT AT ENH NEC 0, /* 0x36 - RShift */ XT AT ENH NEC 0, /* 0x37 - Num* */ XT AT ENH NEC 0, /* 0x38 - LAlt */ XT AT ENH NEC 0, /* 0x39 - Space */ XT AT ENH NEC NECTGL NTTGL 0, /* 0x3A - CapsLock */ XT AT ENH NEC 0, /* 0x3B - F1 */ XT AT ENH NEC 0, /* 0x3C - F2 */ XT AT ENH NEC 0, /* 0x3D - F3 */ XT AT ENH NEC 0, /* 0x3E - F4 */ XT AT ENH NEC 0, /* 0x3F - F5 */ XT AT ENH NEC 0, /* 0x40 - F6 */ XT AT ENH NEC 0, /* 0x41 - F7 */ XT AT ENH NEC 0, /* 0x42 - F8 */ XT AT ENH NEC 0, /* 0x43 - F9 */ XT AT ENH NEC 0, /* 0x44 - F10 */ XT AT ENH 0, /* 0x45 - NumLock */ XT AT ENH 0, /* 0x46 - ScrLock */ XT AT ENH NEC 0, /* 0x47 - Numpad7 */ XT AT ENH NEC 0, /* 0x48 - Numpad8 */ XT AT ENH NEC 0, /* 0x49 - Numpad9 */ XT AT ENH NEC 0, /* 0x4A - Numpad- */ XT AT ENH NEC 0, /* 0x4B - Numpad4 */ XT AT ENH NEC 0, /* 0x4C - Numpad5 */ XT AT ENH NEC 0, /* 0x4D - Numpad6 */ XT AT ENH NEC 0, /* 0x4E - Numpad+ */ XT AT ENH NEC 0, /* 0x4F - Numpad1 */ XT AT ENH NEC 0, /* 0x50 - Numpad2 */ XT AT ENH NEC 0, /* 0x51 - Numpad3 */ XT AT ENH NEC 0, /* 0x52 - Numpad0 */ XT AT ENH NEC 0, /* 0x53 - Numpad. */ 0, /* 0x54 - */ 0, /* 0x55 - */ ENH 0, /* 0x56 - . On UK/Germany keyboards, it is <, > and |. */ ENH NEC 0, /* 0x57 - F11 */ ENH NEC 0, /* 0x58 - F12 */ 0, /* 0x59 - */ 0, /* 0x5A - */ 0, /* 0x5B - */ 0, /* 0x5C - */ 0, /* 0x5D - */ 0, /* 0x5E - */ 0, /* 0x5F - */ 0, /* 0x60 - */ 0, /* 0x61 - */ 0, /* 0x62 - */ 0, /* 0x63 - */ NEC 0, /* 0x64 - F13 */ NEC 0, /* 0x65 - F14 */ NEC 0, /* 0x66 - F15 */ 0, /* 0x67 - */ 0, /* 0x68 - */ 0, /* 0x69 - */ 0, /* 0x6A - */ 0, /* 0x6B - */ 0, /* 0x6C - */ 0, /* 0x6D - */ 0, /* 0x6E - */ 0, /* 0x6F - */ NEC NECTGL NTTGL 0, /* 0x70 - Kana */ 0, /* 0x71 - */ 0, /* 0x72 - */ ENH 0, /* 0x73 - . On Portugese (Brazilian) keyboard, it is /, ? */ 0, /* 0x74 - */ 0, /* 0x75 - */ 0, /* 0x76 - */ 0, /* 0x77 - */ 0, /* 0x78 - */ NEC 0, /* 0x79 - Convert */ 0, /* 0x7A - */ NEC 0, /* 0x7B - Nfer */ 0, /* 0x7C - */ NEC 0, /* 0x7D - Yen */ ENH 0, /* 0x7E - . On Portugese (Brazilian) keyboard, it is keypad . */ 0, /* 0x7F - */ /* Extended keycodes go here */ 0, /* 0x80 - */ 0, /* 0x81 - */ 0, /* 0x82 - */ 0, /* 0x83 - */ 0, /* 0x84 - */ 0, /* 0x85 - */ 0, /* 0x86 - */ 0, /* 0x87 - */ 0, /* 0x88 - */ 0, /* 0x89 - */ 0, /* 0x8A - */ 0, /* 0x8B - */ 0, /* 0x8C - */ NEC 0, /* 0x8D - Num= */ 0, /* 0x8E - */ 0, /* 0x8F - */ ENH NEC 0, /* 0x90 - ^ */ ///Prev Track NEC 0, /* 0x91 - @ */ NEC 0, /* 0x92 - : */ NEC 0, /* 0x93 - _ */ NEC NTTGL 0, /* 0x94 - Xfer - AKA Kanji */ NEC 0, /* 0x95 - Stop */ NEC 0, /* 0x96 - AX */ NEC 0, /* 0x97 - Unlabel'd*/ 0, /* 0x98 - */ /* available for NEC */ ENH 0, /* 0x99 - */ /* available for NEC */ ///Next Track 0, /* 0x9A - */ 0, /* 0x9B - */ ENH 0, /* 0x9C - NumEnter */ ENH 0, /* 0x9D - RCtrl */ 0, /* 0x9E - */ /* available for NEC */ 0, /* 0x9F - */ /* available for NEC */ ENH 0, /* 0xA0 - */ /* available for NEC */ ///Mute ENH 0, /* 0xA1 - */ /* available for NEC */ ///Calculator ENH 0, /* 0xA2 - */ /* available for NEC */ ///Play/Pause 0, /* 0xA3 - */ /* available for NEC */ ENH 0, /* 0xA4 - */ /* available for NEC */ ///Stop 0, /* 0xA5 - */ /* available for NEC */ 0, /* 0xA6 - */ /* available for NEC */ 0, /* 0xA7 - */ 0, /* 0xA8 - */ 0, /* 0xA9 - */ 0, /* 0xAA - */ 0, /* 0xAB - */ 0, /* 0xAC - */ /* available for NEC */ 0, /* 0xAD - */ /* available for NEC */ ENH 0, /* 0xAE - */ /* available for NEC */ ///Volume - 0, /* 0xAF - */ /* available for NEC */ ENH 0, /* 0xB0 - */ /* available for NEC */ ///Volume + 0, /* 0xB1 - */ /* available for NEC */ ENH 0, /* 0xB2 - */ /* available for NEC */ ///Web/Home NEC 0, /* 0xB3 - Num, */ 0, /* 0xB4 - */ ENH NEC 0, /* 0xB5 - Num/ */ 0, /* 0xB6 - */ AT ENH NEC 0, /* 0xB7 - SysRq */ ENH 0, /* 0xB8 - RAlt */ 0, /* 0xB9 - */ 0, /* 0xBA - */ 0, /* 0xBB - */ 0, /* 0xBC - */ 0, /* 0xBD - */ 0, /* 0xBE - */ 0, /* 0xBF - */ 0, /* 0xC0 - */ 0, /* 0xC1 - */ 0, /* 0xC2 - */ 0, /* 0xC3 - */ 0, /* 0xC4 - */ ENH 0, /* 0xC5 - Pause */ 0, /* 0xC6 - */ ENH NEC 0, /* 0xC7 - Home */ ENH NEC 0, /* 0xC8 - UpArrow */ ENH NEC 0, /* 0xC9 - PgUp */ 0, /* 0xCA - */ ENH NEC 0, /* 0xCB - LtArrow */ 0, /* 0xCC - */ ENH NEC 0, /* 0xCD - RtArrow */ 0, /* 0xCE - */ ENH NEC 0, /* 0xCF - End */ ENH NEC 0, /* 0xD0 - DnArrow */ ENH NEC 0, /* 0xD1 - PgDn */ ENH NEC 0, /* 0xD2 - Insert */ ENH NEC 0, /* 0xD3 - Delete */ 0, /* 0xD4 - */ 0, /* 0xD5 - */ 0, /* 0xD6 - */ 0, /* 0xD7 - */ 0, /* 0xD8 - */ 0, /* 0xD9 - */ 0, /* 0xDA - */ ENH NEC 0, /* 0xDB - LWin */ ENH NEC 0, /* 0xDC - RWin */ ENH NEC 0, /* 0xDD - AppMenu */ ENH 0, /* 0xDE - Power */ ENH 0, /* 0xDF - Sleep */ 0, /* 0xE0 - */ 0, /* 0xE1 - */ 0, /* 0xE2 - */ ENH 0, /* 0xE3 - Wake */ 0, /* 0xE4 - */ ENH 0, /* 0xE5 - */ ///Search ENH 0, /* 0xE6 - */ ///Favorites ENH 0, /* 0xE7 - */ ///Refresh ENH 0, /* 0xE8 - */ ///Stop ENH 0, /* 0xE9 - */ ///Forward ENH 0, /* 0xEA - */ ///Back ENH 0, /* 0xEB - */ ///My Computer ENH 0, /* 0xEC - */ ///Mail ENH 0, /* 0xED - */ ///Media 0, /* 0xEE - */ 0, /* 0xEF - */ 0, /* 0xF0 - */ 0, /* 0xF1 - */ 0, /* 0xF2 - */ 0, /* 0xF3 - */ 0, /* 0xF4 - */ 0, /* 0xF5 - */ 0, /* 0xF6 - */ 0, /* 0xF7 - */ 0, /* 0xF8 - */ 0, /* 0xF9 - */ 0, /* 0xFA - */ 0, /* 0xFB - */ 0, /* 0xFC - */ 0, /* 0xFD - */ 0, /* 0xFE - */ 0, /* 0xFF - */ }; #undef XT #undef AT #undef ENH #undef NEC /***************************************************************************** * * @doc INTERNAL * * @struct CKbd | * * The object for the * generic keyboard. * * @field IDirectInputDeviceCalllback | didc | * * The object (containing vtbl). * * @field PMKBDSTAT | pksPhys | * * Pointer to physical keyboard status information kept down in the * VxD. * * @field VXDINSTANCE * | pvi | * * The DirectInput instance handle. * * @field DWORD | dwKbdType | * * The device subtype for this keyboard. * * @field DWORD | flEmulation | * * The emulation flags forced by the application. If any of * these flags is set (actually, at most one will be set), then * we are an alias device. * * @field DIDATAFORMAT | df | * * The dynamically-generated data format based on the * keyboard type. * * @field DIOBJECTDATAFORMAT | rgodf[] | * * Object data format table generated as part of the * . * * @comm * * It is the caller's responsibility to serialize access as * necessary. * *****************************************************************************/ typedef struct CKbd { /* Supported interfaces */ IDirectInputDeviceCallback dcb; PKBDSTAT pksPhys; VXDINSTANCE *pvi; DWORD dwKbdType; DWORD flEmulation; DIDATAFORMAT df; DIOBJECTDATAFORMAT rgodf[DIKBD_CKEYS]; } CKbd, DK, *PDK; #define ThisClass CKbd #define ThisInterface IDirectInputDeviceCallback #define riidExpected &IID_IDirectInputDeviceCallback /***************************************************************************** * * CKbd::QueryInterface (from IUnknown) * CKbd::AddRef (from IUnknown) * CKbd::Release (from IUnknown) * *****************************************************************************/ /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CKbd | QueryInterface | * * Gives a client access to other interfaces on an object. * * @parm IN REFIID | riid | * * The requested interface's IID. * * @parm OUT LPVOID * | ppvObj | * * Receives a pointer to the obtained interface. * * @returns * * Returns a COM error code. * * @xref OLE documentation for . * ***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CKbd | AddRef | * * Increments the reference count for the interface. * * @returns * * Returns the object reference count. * * @xref OLE documentation for . * ***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CKbd | Release | * * Decrements the reference count for the interface. * If the reference count on the object falls to zero, * the object is freed from memory. * * @returns * * Returns the object reference count. * * @xref OLE documentation for . * ***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CKbd | QIHelper | * * We don't have any dynamic interfaces and simply forward * to . * * @parm IN REFIID | riid | * * The requested interface's IID. * * @parm OUT LPVOID * | ppvObj | * * Receives a pointer to the obtained interface. * ***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CKbd | AppFinalize | * * We don't have any weak pointers, so we can just * forward to . * * @parm PV | pvObj | * * Object being released from the application's perspective. * *****************************************************************************/ #ifdef DEBUG Default_QueryInterface(CKbd) Default_AddRef(CKbd) Default_Release(CKbd) #else #define CKbd_QueryInterface Common_QueryInterface #define CKbd_AddRef Common_AddRef #define CKbd_Release Common_Release #endif #define CKbd_QIHelper Common_QIHelper #define CKbd_AppFinalize Common_AppFinalize /***************************************************************************** * * @doc INTERNAL * * @func void | CKbd_Finalize | * * Releases the resources of the device. * * @parm PV | pvObj | * * Object being released. Note that it may not have been * completely initialized, so everything should be done * carefully. * *****************************************************************************/ void INTERNAL CKbd_Finalize(PV pvObj) { PDK this = pvObj; if (this->pvi) { HRESULT hres; hres = Hel_DestroyInstance(this->pvi); AssertF(SUCCEEDED(hres)); } } /***************************************************************************** * * @doc INTERNAL * * @func int | WrappedGetKeyboardType | * * GetKeyboardType but wrapped in DEBUG for registry overrides. * * @parm int | nTypeFlag | * * Which data to return. Only 0, 1 and 2 are supported * * @returns * * int value requested * *****************************************************************************/ #ifndef DEBUG #ifdef USE_WM_INPUT #define WrappedGetKeyboardType(x) DIRaw_GetKeyboardType(x) #else #define WrappedGetKeyboardType(x) GetKeyboardType(x) #endif #else int INTERNAL WrappedGetKeyboardType ( int nTypeFlag ) { TCHAR ValueName[2]; int TypeRes; #ifdef USE_WM_INPUT TypeRes = DIRaw_GetKeyboardType( nTypeFlag ); #else TypeRes = GetKeyboardType( nTypeFlag ); #endif if( nTypeFlag < 10 ) { ValueName[0] = TEXT( '0' ) + nTypeFlag; ValueName[1] = TEXT( '\0' ); TypeRes = (int)RegQueryDIDword( REGSTR_KEY_KEYBTYPE, ValueName, (DWORD)TypeRes ); SquirtSqflPtszV(sqfl | sqflTrace, TEXT( "DINPUT: GetKeyboardType(%d) returning 0x%08x" ), nTypeFlag, TypeRes ); } else { RPF( "Somebody is passing %d to WrappedGetKeyboardType", nTypeFlag ); } return TypeRes; } #endif /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CKbd | Acquire | * * Tell the device driver to begin data acquisition. * * It is the caller's responsibility to have set the * data format before obtaining acquisition. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * : The operation was begun and should be completed * by the caller by communicating with the . * *****************************************************************************/ STDMETHODIMP CKbd_Acquire(PDICB pdcb) { VXDDWORDDATA vdd; PDK this; HRESULT hres; /* * This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb); /* * Propagate the state of the potential toggle keys down to * the VxD. This also alerts the VxD that acquisition is coming, * so it can reset the state tables if necessary. */ vdd.pvi = this->pvi; vdd.dw = 0; if( WrappedGetKeyboardType(0) == 7 ) { /* * Let the keyboard driver know that this is an FE keyboard */ vdd.dw |= 16; if (GetAsyncKeyState(VK_KANA) < 0) { vdd.dw |= 1; } if (GetAsyncKeyState(VK_CAPITAL) < 0) { vdd.dw |= 2; } if (GetAsyncKeyState(VK_KANJI) < 0) { vdd.dw |= 8; } } if( this->pvi->fl & VIFL_CAPTURED ) { vdd.dw |= 4; // Tell the keyboard driver to pre-acquire hooks } hres = Hel_Kbd_InitKeys(&vdd); if( this->pvi->fl & VIFL_CAPTURED ) { /* * A bit of work needs to be done at ring 3 now. * Try to clear any key that is set. Start with VK_BACK as mouse * buttons and undefined things go before. * This still covers a lot of undefined VKs but we're less likely * to do damage clearing something that was undefined than leaving * keys uncleared. */ BYTE vk; for( vk=VK_BACK; vkpvi); *ppvi = (PV)this->pvi; hres = S_OK; ExitOleProcPpvR(ppvi); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CKbd | GetDataFormat | * * Obtains the device's preferred data format. * * @parm OUT LPDIDEVICEFORMAT * | ppdf | * * to receive pointer to device format. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * = : The *

parameter is not a valid pointer. * *****************************************************************************/ STDMETHODIMP CKbd_GetDataFormat(PDICB pdcb, LPDIDATAFORMAT *ppdf) { HRESULT hres; PDK this; EnterProcI(IDirectInputDeviceCallback::Kbd::GetDataFormat, (_ "p", pdcb)); /* * This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb); *ppdf = &this->df; hres = S_OK; ExitOleProcPpvR(ppdf); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CKbd | GetDeviceInfo | * * Obtain general information about the device. * * @parm OUT LPDIDEVICEINSTANCEW | pdiW | * * to be filled in. The * and * have already been filled in. * * Secret convenience: is equal * to . * *****************************************************************************/ STDMETHODIMP CKbd_GetDeviceInfo(PDICB pdcb, LPDIDEVICEINSTANCEW pdiW) { HRESULT hres; PDK this; EnterProcI(IDirectInputDeviceCallback::Kbd::GetDeviceInfo, (_ "pp", pdcb, pdiW)); /* * This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb); AssertF(IsValidSizeDIDEVICEINSTANCEW(pdiW->dwSize)); AssertF(IsEqualGUID(&GUID_SysKeyboard , &pdiW->guidInstance) || IsEqualGUID(&GUID_SysKeyboardEm , &pdiW->guidInstance) || IsEqualGUID(&GUID_SysKeyboardEm2, &pdiW->guidInstance)); pdiW->guidProduct = GUID_SysKeyboard; pdiW->dwDevType = MAKE_DIDEVICE_TYPE(DIDEVTYPE_KEYBOARD, this->dwKbdType); LoadStringW(g_hinst, IDS_STDKEYBOARD, pdiW->tszProductName, cA(pdiW->tszProductName)); LoadStringW(g_hinst, IDS_STDKEYBOARD, pdiW->tszInstanceName, cA(pdiW->tszInstanceName)); hres = S_OK; ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method void | CKbd | GetCapabilities | * * Get keyboard device capabilities. * * @parm LPDIDEVCAPS | pdc | * * Device capabilities structure to receive result. * * @returns * on success. * *****************************************************************************/ STDMETHODIMP CKbd_GetCapabilities(PDICB pdcb, LPDIDEVCAPS pdc) { HRESULT hres; PDK this; EnterProcI(IDirectInputDeviceCallback::Kbd::GetCapabilities, (_ "pp", pdcb, pdc)); /* * This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb); pdc->dwDevType = MAKE_DIDEVICE_TYPE(DIDEVTYPE_KEYBOARD, this->dwKbdType); pdc->dwFlags = DIDC_ATTACHED; if (this->flEmulation) { pdc->dwFlags |= DIDC_ALIAS; } // Remove these assertions for 32650 // AssertF(pdc->dwAxes == 0); // AssertF(pdc->dwPOVs == 0); pdc->dwButtons = this->df.dwNumObjs; hres = S_OK; ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method void | CKbd | GetPhysicalState | * * Read the physical keyboard state into

. * * Note that it doesn't matter if this is not atomic. * If a key goes down or up while we are reading it, * we will get a mix of old and new data. No big deal. * * @parm PDK | this | * * The object in question. * * @parm PKBDSTATE | pksOut | * * Where to put the keyboard state. * @returns * None. * *****************************************************************************/ void INLINE CKbd_GetPhysicalState(PDK this, PKBDSTAT pksOut) { AssertF(this->pksPhys); *pksOut = *this->pksPhys; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CKbd | GetDeviceState | * * Obtains the state of the keyboard device. * * It is the caller's responsibility to have validated all the * parameters and ensure that the device has been acquired. * * @parm OUT LPVOID | lpvData | * * Keyboard data in the preferred data format. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * = : The *

parameter is not a valid pointer. * *****************************************************************************/ STDMETHODIMP CKbd_GetDeviceState(PDICB pdcb, LPVOID pvData) { HRESULT hres; PDK this; PKBDSTAT pkstOut = pvData; EnterProcI(IDirectInputDeviceCallback::Kbd::GetDeviceState, (_ "pp", pdcb, pvData)); /* * This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb); /* * ISSUE-2001/03/29-timgill older apps may need compat behaviour * We never used to check whether or not the device was still * acquired since without exclusive mode there would be no reason for * the device not to be. * To keep behavior the same for older apps it might be better to * only fail if VIFL_CAPTURED is not set but just checking VIFL_ACQUIRED * is good enough for now, maybe for ever. */ // if( !(this->pvi->fl & VIFL_CAPTURED) // || (this->pvi->fl & VIFL_ACQUIRED) ) if( this->pvi->fl & VIFL_ACQUIRED ) { CKbd_GetPhysicalState(this, pkstOut); hres = S_OK; } else { RPF( "Keyboard VxD flags: 0x%08x", this->pvi->fl ); hres = DIERR_INPUTLOST; } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CKbd | GetObjectInfo | * * Obtain the friendly name of an object, passwed by index * into the preferred data format. * * @parm IN LPCDIPROPINFO | ppropi | * * Information describing the object being accessed. * * @parm IN OUT LPDIDEVICEOBJECTINSTANCEW | pdidioiW | * * Structure to receive information. The * , * , * and * * fields have already been filled in. * * @returns * * Returns a COM error code. * *****************************************************************************/ STDMETHODIMP CKbd_GetObjectInfo(PDICB pdcb, LPCDIPROPINFO ppropi, LPDIDEVICEOBJECTINSTANCEW pdidoiW) { HRESULT hres; PDK this; EnterProcI(IDirectInputDeviceCallback::Kbd::GetObjectInfo, (_ "pxp", pdcb, ppropi->iobj, pdidoiW)); /* * This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb); #ifdef HAVE_DIDEVICEOBJECTINSTANCE_DX5 AssertF(IsValidSizeDIDEVICEOBJECTINSTANCEW(pdidoiW->dwSize)); #endif if (ppropi->iobj < this->df.dwNumObjs) { AssertF(this->rgodf == this->df.rgodf); AssertF(ppropi->dwDevType == this->rgodf[ppropi->iobj].dwType); AssertF(ppropi->dwDevType & DIDFT_BUTTON); LoadStringW(g_hinst, IDS_KEYBOARDOBJECT + DIDFT_GETINSTANCE(ppropi->dwDevType), pdidoiW->tszName, cA(pdidoiW->tszName)); /* * We do not support force feedback on keyboards, so * there are no FF flags to report. */ hres = S_OK; } else { hres = E_INVALIDARG; } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CKbd | SetCooperativeLevel | * * Notify the device of the cooperative level. * * @parm IN HWND | hwnd | * * The window handle. * * @parm IN DWORD | dwFlags | * * The cooperativity level. We do not support exclusive access. * * @returns * * Returns a COM error code. * *****************************************************************************/ STDMETHODIMP CKbd_SetCooperativeLevel(PDICB pdcb, HWND hwnd, DWORD dwFlags) { HRESULT hres; PDK this; EnterProcI(IDirectInputDeviceCallback::Kbd::SetCooperativityLevel, (_ "pxx", pdcb, hwnd, dwFlags)); /* * This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb); AssertF(this->pvi); AssertF(DIGETEMFL(this->pvi->fl) == 0 || DIGETEMFL(this->pvi->fl) == DIEMFL_KBD || DIGETEMFL(this->pvi->fl) == DIEMFL_KBD2); /* * We don't allow background exclusive access. * This is actually not a real problem to support; we just don't feel like it * because it's too dangerous. */ if (!(this->pvi->fl & DIMAKEEMFL(DIEMFL_KBD2))) { if (dwFlags & DISCL_EXCLUSIVE) { if (dwFlags & DISCL_FOREGROUND) { this->pvi->fl |= VIFL_CAPTURED; this->pvi->fl &= ~VIFL_NOWINKEY; hres = S_OK; } else { /* Disallow exclusive background */ hres = E_NOTIMPL; } } else { this->pvi->fl &= ~VIFL_CAPTURED; this->pvi->fl &= ~VIFL_NOWINKEY; hres = S_OK; if (dwFlags & DISCL_NOWINKEY) { if (dwFlags & DISCL_FOREGROUND) { this->pvi->fl |= VIFL_NOWINKEY; } else { RPF("Kbd::SetCooperativeLevel: NOWINKEY not supported in Backgroud mode."); hres = E_NOTIMPL; } } } } else { /* * Emulation level 2 does not support background access. */ if ((this->pvi->fl & DIMAKEEMFL(DIEMFL_KBD2)) && (dwFlags & DISCL_BACKGROUND)) { hres = E_NOTIMPL; } else { this->pvi->fl &= ~VIFL_NOWINKEY; hres = S_OK; if (dwFlags & DISCL_NOWINKEY) { if (dwFlags & DISCL_FOREGROUND) { this->pvi->fl |= VIFL_NOWINKEY; } else { RPF("Kbd::SetCooperativeLevel: NOWINKEY not supported in Backgroud mode."); hres = E_NOTIMPL; } } } } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CKbd | RunControlPanel | * * Run the keyboard control panel. * * @parm IN HWND | hwndOwner | * * The owner window. * * @parm DWORD | dwFlags | * * Flags. * *****************************************************************************/ #pragma BEGIN_CONST_DATA TCHAR c_tszKeyboard[] = TEXT("keyboard"); #pragma END_CONST_DATA STDMETHODIMP CKbd_RunControlPanel(PDICB pdcb, HWND hwnd, DWORD dwFlags) { HRESULT hres; PDK this; EnterProcI(IDirectInputDeviceCallback::Kbd::RunControlPanel, (_ "pxx", pdcb, hwnd, dwFlags)); /* * This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb); hres = hresRunControlPanel(c_tszKeyboard); ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method DWORD | CKbd | InitJapanese | * * Initialize the Japanese keyboard goo. * * Annoying quirk! On Windows 95, Japanese keyboards generate * their own scan codes. But on Windows NT, they generate * "nearly AT-compatible" scan codes. * * @returns * * KBDTYPE_ANYKBD or KBDTYPE_ANYKBD + KBDTYPE_NECTGL. * *****************************************************************************/ DWORD INTERNAL CKbd_InitJapanese(PDK this, PVXDDEVICEFORMAT pdevf) { DWORD dwSubType; UINT idKbd; DWORD dwRc; dwSubType = WrappedGetKeyboardType(1); if (HIBYTE(dwSubType) == 0x0D) { /* NEC PC98 series */ switch (LOBYTE(dwSubType)) { case 1: default: idKbd = IDDATA_KBD_NEC98; this->dwKbdType = DIDEVTYPEKEYBOARD_NEC98; dwRc = KBDTYPE_ANYKBD + KBDTYPE_NECTGL; break; case 4: idKbd = IDDATA_KBD_NEC98LAPTOP; this->dwKbdType = DIDEVTYPEKEYBOARD_NEC98LAPTOP; dwRc = KBDTYPE_ANYKBD + KBDTYPE_NECTGL; break; case 5: idKbd = IDDATA_KBD_NEC98_106; this->dwKbdType = DIDEVTYPEKEYBOARD_NEC98106; dwRc = KBDTYPE_ANYKBD + KBDTYPE_NECTGL; break; } /* * If the scan code for ESC is 1, then we're on an * NEC98 keyboard that acts AT-like. */ CAssertF(IDDATA_KBD_NEC98_NT - IDDATA_KBD_NEC98 == IDDATA_KBD_NEC98LAPTOP_NT - IDDATA_KBD_NEC98LAPTOP); CAssertF(IDDATA_KBD_NEC98_NT - IDDATA_KBD_NEC98 == IDDATA_KBD_NEC98_106_NT - IDDATA_KBD_NEC98_106); if (MapVirtualKey(VK_ESCAPE, 0) == DIK_ESCAPE) { idKbd += IDDATA_KBD_NEC98_NT - IDDATA_KBD_NEC98; } } else { switch (dwSubType) { case 0: this->dwKbdType = DIDEVTYPEKEYBOARD_PCENH; dwRc = KBDTYPE_ENH; goto done; /* Yuck */ case 1: idKbd = IDDATA_KBD_JAPANAX; this->dwKbdType = DIDEVTYPEKEYBOARD_JAPANAX; dwRc = KBDTYPE_ANYKBD; break; case 13: case 14: case 15: idKbd = IDDATA_KBD_J3100; this->dwKbdType = DIDEVTYPEKEYBOARD_J3100; dwRc = KBDTYPE_ANYKBD; break; case 4: /* Rumored to be Epson */ case 5: /* Rumored to be Fujitsu */ case 7: /* Rumored to be IBMJ */ case 10: /* Rumored to be Matsushita */ case 18: /* Rumored to be Toshiba */ default: idKbd = IDDATA_KBD_JAPAN106; this->dwKbdType = DIDEVTYPEKEYBOARD_JAPAN106; dwRc = KBDTYPE_ANYKBD; break; } } if( fWinnt ) { /* * ISSUE-2001/03/29-timgill Japanese keyboard assumption needs testing * All Japanese keyboards on NT have toggle keys * Except subtype zero? Needs test */ dwRc = KBDTYPE_ANYKBD + KBDTYPE_NTTGL; } /* * Now load up the translation table goo. */ pdevf->dwExtra = (UINT_PTR)pvFindResource(g_hinst, idKbd, RT_RCDATA); if (pdevf->dwExtra == 0) { dwRc = 0; } done:; return dwRc; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CKbd | Init | * * Initialize the object by establishing the data format * based on the keyboard type. Anything we don't recognize, * we treat as a PC Enhanced keyboard. * * @parm REFGUID | rguid | * * The instance GUID we are being asked to create. * *****************************************************************************/ HRESULT INTERNAL CKbd_Init(PDK this, REFGUID rguid) { DWORD dwDevType; UINT ib; HRESULT hres; VXDDEVICEFORMAT devf; EnterProc(CKbd_Init, (_ "pG", this, rguid)); #ifdef DEBUG /* * Check that the Japan tables aren't messed up. */ { UINT idk; for (idk = IDDATA_KBD_NEC98; idk <= IDDATA_KBD_J3100; idk++) { BYTE rgb[DIKBD_CKEYS]; HANDLE hrsrc; LPBYTE pb; ZeroX(rgb); /* * Make sure the table exists. */ hrsrc = FindResource(g_hinst, (LPTSTR)(LONG_PTR)idk, RT_RCDATA); AssertF(hrsrc); pb = LoadResource(g_hinst, hrsrc); /* * Walk the table and make sure each thing that exists * in the translation table also exists in our master table. * Also make sure that it isn't a dup with something else * in the same table. */ /* * Note, however, that the JAPAN106 keyboard contains * dups so we can save having to write an entire * translation table. And then NEC98_NT tables contain * lots of dups out of sheer laziness. */ for (ib = 0; ib < DIKBD_CKEYS; ib++) { if (pb[ib]) { AssertF(c_rgktWhich[pb[ib]] & KBDTYPE_ANYKBD); AssertF(fLorFF(idk == IDDATA_KBD_JAPAN106 && ib == 0x73, rgb[pb[ib]] == 0)); rgb[pb[ib]] = 1; } } } } #endif this->df.dwSize = cbX(DIDATAFORMAT); this->df.dwObjSize = cbX(DIOBJECTDATAFORMAT); this->df.dwDataSize = sizeof(KBDSTAT); this->df.rgodf = this->rgodf; this->dwKbdType = WrappedGetKeyboardType(0); /* * Create the object with the most optimistic data format. * This allows apps to access new keys without having to rev DINPUT. * * However, leave out the following scan codes because some keyboards * generate them spuriously: * * 0xB6 * * If you hold the right shift key and then press an * extended arrow key, then release both, some keyboards * generate the following: * * 0x36 - right shift down * 0xE0 0xB6 - extended right shift up (?) * 0xE0 0x4B - extended left arrow down * 0xE0 0xCB - extended left arrow up * 0xE0 0x36 - extended right shift down (?) * 0xE6 - right shift up * * The stray 0xE0 0x36 needs to be ignored. * * 0xAA * * Same as 0xB6, but with the left shift key. * * */ for (ib = 0; ib < DIKBD_CKEYS; ib++) { if (ib != 0xAA && ib != 0xB6) { this->rgodf[ib].pguid = &GUID_Key; this->rgodf[ib].dwOfs = ib; this->rgodf[ib].dwType = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE(ib); AssertF(this->rgodf[ib].dwFlags == 0); } } devf.cObj = DIKBD_CKEYS; devf.cbData = cbX(KBDSTAT); devf.rgodf = this->rgodf; /* * But first a word from our sponsor: Figure out if this keyboard * needs a translation table. */ devf.dwExtra = 0; /* Assume no translation */ if (this->dwKbdType != 7) { /* Not a yucky Japanese keyboard */ switch (this->dwKbdType) { case DIDEVTYPEKEYBOARD_PCXT: dwDevType = KBDTYPE_XT; break; case DIDEVTYPEKEYBOARD_PCAT: dwDevType = KBDTYPE_AT; break; default: case DIDEVTYPEKEYBOARD_PCENH: dwDevType = KBDTYPE_ENH; break; } } else { /* Yucky Japanese keyboard */ dwDevType = CKbd_InitJapanese(this, &devf); if (!dwDevType) { goto justfail; } } /* * And now a word from our other sponsor: Figure out the * emulation flags based on the GUID. */ AssertF(GUID_SysKeyboard .Data1 == 0x6F1D2B61); AssertF(GUID_SysKeyboardEm .Data1 == 0x6F1D2B82); AssertF(GUID_SysKeyboardEm2.Data1 == 0x6F1D2B83); switch (rguid->Data1) { default: case 0x6F1D2B61: AssertF(IsEqualGUID(rguid, &GUID_SysKeyboard)); AssertF(this->flEmulation == 0); break; case 0x6F1D2B82: AssertF(IsEqualGUID(rguid, &GUID_SysKeyboardEm)); this->flEmulation = DIEMFL_KBD; break; case 0x6F1D2B83: AssertF(IsEqualGUID(rguid, &GUID_SysKeyboardEm2)); this->flEmulation = DIEMFL_KBD2; break; } devf.dwEmulation = this->flEmulation; //RPF("CKbd_Init: Kbd type: %d, subtype: %d, dwEmulation: %d", GetKeyboardType(0), GetKeyboardType(1), devf.dwEmulation); hres = Hel_Kbd_CreateInstance(&devf, &this->pvi); if (SUCCEEDED(hres)) { UINT cobj; BYTE rgbSeen[DIKBD_CKEYS]; AssertF(this->pvi); AssertF(this->df.dwFlags == 0); AssertF(this->df.dwNumObjs == 0); /* * Japanese keyboards have many-to-one mappings, so * we need to filter out the dups or we end up in big * trouble. */ ZeroX(rgbSeen); /* * Now create the real data format. * * We shadow this->df.dwNumObjs in cobj so that the compiler * can enregister it. * * Note that we filter through the translation table if there * is one. */ cobj = 0; for (ib = 0; ib < DIKBD_CKEYS; ib++) { BYTE bScan = devf.dwExtra ? ((LPBYTE)devf.dwExtra)[ib] : ib; if ((c_rgktWhich[bScan] & dwDevType) && !rgbSeen[bScan]) { PODF podf = &this->rgodf[cobj]; rgbSeen[bScan] = 1; podf->pguid = &GUID_Key; podf->dwOfs = bScan; if (c_rgktWhich[bScan] & dwDevType & (KBDTYPE_NECTGL|KBDTYPE_NTTGL) ) { podf->dwType = DIDFT_TGLBUTTON | DIDFT_MAKEINSTANCE(bScan); } else { podf->dwType = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE(bScan); } AssertF(podf->dwFlags == 0); cobj++; this->df.dwNumObjs++; } } this->pksPhys = this->pvi->pState; } else { justfail:; hres = E_FAIL; } ExitOleProc(); return hres; } /***************************************************************************** * * CKbd_New (constructor) * *****************************************************************************/ STDMETHODIMP CKbd_New(PUNK punkOuter, REFGUID rguid, RIID riid, PPV ppvObj) { HRESULT hres; EnterProcI(IDirectInputDeviceCallback::Kbd::, (_ "Gp", riid, ppvObj)); AssertF(IsEqualGUID(rguid, &GUID_SysKeyboard) || IsEqualGUID(rguid, &GUID_SysKeyboardEm) || IsEqualGUID(rguid, &GUID_SysKeyboardEm2)); hres = Common_NewRiid(CKbd, punkOuter, riid, ppvObj); if (SUCCEEDED(hres)) { /* Must use _thisPv in case of aggregation */ PDK this = _thisPv(*ppvObj); if (SUCCEEDED(hres = CKbd_Init(this, rguid))) { } else { Invoke_Release(ppvObj); } } ExitOleProcPpvR(ppvObj); return hres; } /***************************************************************************** * * The long-awaited vtbls and templates * *****************************************************************************/ #pragma BEGIN_CONST_DATA #define CKbd_Signature 0x2044424B /* "KBD " */ Interface_Template_Begin(CKbd) Primary_Interface_Template(CKbd, IDirectInputDeviceCallback) Interface_Template_End(CKbd) Primary_Interface_Begin(CKbd, IDirectInputDeviceCallback) CKbd_GetInstance, CDefDcb_GetVersions, CKbd_GetDataFormat, CKbd_GetObjectInfo, CKbd_GetCapabilities, CKbd_Acquire, CDefDcb_Unacquire, CKbd_GetDeviceState, CKbd_GetDeviceInfo, CDefDcb_GetProperty, CDefDcb_SetProperty, CDefDcb_SetEventNotification, CKbd_SetCooperativeLevel, CKbd_RunControlPanel, CDefDcb_CookDeviceData, CDefDcb_CreateEffect, CDefDcb_GetFFConfigKey, CDefDcb_SendDeviceData, CDefDcb_Poll, CDefDcb_GetUsage, CDefDcb_MapUsage, CDefDcb_SetDIData, Primary_Interface_End(CKbd, IDirectInputDeviceCallback)