1 /* 2 The zlib/libpng License 3 4 Copyright (c) 2005-2007 Phillip Castaneda (pjcast -- www.wreckedgames.com) 5 6 This software is provided 'as-is', without any express or implied warranty. In no event will 7 the authors be held liable for any damages arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, including commercial 10 applications, and to alter it and redistribute it freely, subject to the following 11 restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not claim that 14 you wrote the original software. If you use this software in a product, 15 an acknowledgment in the product documentation would be appreciated but is 16 not required. 17 18 2. Altered source versions must be plainly marked as such, and must not be 19 misrepresented as being the original software. 20 21 3. This notice may not be removed or altered from any source distribution. 22 */ 23 #include "win32/Win32InputManager.h" 24 #include "win32/Win32KeyBoard.h" 25 #include "OISException.h" 26 #include "OISEvents.h" 27 #include <sstream> 28 29 using namespace OIS; 30 31 //--------------------------------------------------------------------------------------------------// 32 Win32Keyboard::Win32Keyboard( InputManager* creator, IDirectInput8* pDI, bool buffered, DWORD coopSettings ) 33 : Keyboard(creator->inputSystemName(), buffered, 0, creator) 34 { 35 mKeyboard = 0; 36 mDirectInput = pDI; 37 coopSetting = coopSettings; 38 39 //Clear our keyboard state buffer 40 memset( &KeyBuffer, 0, 256 ); 41 deadKey = '\0'; 42 static_cast<Win32InputManager*>(mCreator)->_setKeyboardUsed(true); 43 } 44 45 //--------------------------------------------------------------------------------------------------// 46 void Win32Keyboard::_initialize() 47 { 48 mModifiers = 0; 49 deadKey = '\0'; 50 51 if(FAILED(mDirectInput->CreateDevice(GUID_SysKeyboard, &mKeyboard, NULL))) 52 OIS_EXCEPT( E_General, "Win32Keyboard::Win32Keyboard >> Could not init device!"); 53 54 if(FAILED(mKeyboard->SetDataFormat(&c_dfDIKeyboard))) 55 OIS_EXCEPT( E_General, "Win32Keyboard::Win32Keyboard >> format error!"); 56 57 HWND hwin = ((Win32InputManager*)mCreator)->getWindowHandle(); 58 59 if(FAILED(mKeyboard->SetCooperativeLevel( hwin, coopSetting))) 60 OIS_EXCEPT( E_General, "Win32Keyboard::Win32Keyboard >> coop error!"); 61 62 if( mBuffered ) 63 { 64 DIPROPDWORD dipdw; 65 dipdw.diph.dwSize = sizeof(DIPROPDWORD); 66 dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); 67 dipdw.diph.dwObj = 0; 68 dipdw.diph.dwHow = DIPH_DEVICE; 69 dipdw.dwData = KEYBOARD_DX_BUFFERSIZE; 70 71 if (FAILED(mKeyboard->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ))) 72 OIS_EXCEPT( E_General, "Win32Keyboard::Win32Keyboard >> buffer error!"); 73 } 74 75 HRESULT hr = mKeyboard->Acquire(); 76 if(FAILED(hr) && hr != DIERR_OTHERAPPHASPRIO) 77 OIS_EXCEPT( E_General, "Win32Keyboard::Win32Keyboard >> aquire error!"); 78 } 79 80 //--------------------------------------------------------------------------------------------------// 81 Win32Keyboard::~Win32Keyboard() 82 { 83 if(mKeyboard) 84 { 85 mKeyboard->Unacquire(); 86 mKeyboard->Release(); 87 mKeyboard = 0; 88 } 89 static_cast<Win32InputManager*>(mCreator)->_setKeyboardUsed(false); 90 } 91 92 //--------------------------------------------------------------------------------------------------// 93 void Win32Keyboard::capture() 94 { 95 if( mBuffered ) 96 _readBuffered(); 97 else 98 _read(); 99 } 100 101 //--------------------------------------------------------------------------------------------------// 102 void Win32Keyboard::_readBuffered() 103 { 104 DIDEVICEOBJECTDATA diBuff[KEYBOARD_DX_BUFFERSIZE]; 105 DWORD entries = KEYBOARD_DX_BUFFERSIZE; 106 HRESULT hr; 107 //Only one keyboard allowed per app, so static is ok 108 static bool verifyAfterAltTab = false; 109 110 hr = mKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), diBuff, &entries, 0 ); 111 if( hr != DI_OK ) 112 { 113 hr = mKeyboard->Acquire(); 114 if (hr == E_ACCESSDENIED) 115 verifyAfterAltTab = true; 116 117 while( hr == DIERR_INPUTLOST ) 118 hr = mKeyboard->Acquire(); 119 120 return; 121 } 122 123 if( FAILED(hr) ) 124 OIS_EXCEPT( E_General, "Win32Keyboard::_readBuffered() >> Problem with Device!" ); 125 126 //Update keyboard and modifier states.. And, if mListener, fire events 127 for(unsigned int i = 0; i < entries; ++i ) 128 { 129 //If the listener returns false, that means that we are probably deleted... 130 //send no more events and just leave as the this pointer is invalid now... 131 bool ret = true; 132 KeyCode kc = (KeyCode)diBuff[ i ].dwOfs; 133 134 //Store result in our keyBuffer too 135 KeyBuffer[kc] = static_cast<unsigned char>(diBuff[ i ].dwData); 136 137 if( diBuff[ i ].dwData & 0x80 ) 138 { 139 //Turn on modifier 140 if( kc == KC_LCONTROL || kc == KC_RCONTROL ) 141 mModifiers |= Ctrl; 142 else if( kc == KC_LSHIFT || kc == KC_RSHIFT ) 143 mModifiers |= Shift; 144 else if( kc == KC_LMENU || kc == KC_RMENU ) 145 mModifiers |= Alt; 146 147 if( mListener ) 148 ret = mListener->keyPressed( KeyEvent( this, kc, _translateText(kc) ) ); 149 } 150 else 151 { 152 //Turn off modifier 153 if( kc == KC_LCONTROL || kc == KC_RCONTROL ) 154 mModifiers &= ~Ctrl; 155 else if( kc == KC_LSHIFT || kc == KC_RSHIFT ) 156 mModifiers &= ~Shift; 157 else if( kc == KC_LMENU || kc == KC_RMENU ) 158 mModifiers &= ~Alt; 159 160 //Fire off event 161 if( mListener ) 162 ret = mListener->keyReleased( KeyEvent( this, kc, 0 ) ); 163 } 164 165 if(ret == false) 166 break; 167 } 168 169 // If a lost device/access denied was detected, recover gracefully with new events 170 if(verifyAfterAltTab) 171 { 172 bool ret = true; 173 174 //Copy old buffer to temp location to compare against 175 unsigned char keyBufferCopy[256]; 176 memcpy(keyBufferCopy, KeyBuffer, 256); 177 178 //Update new state 179 _read(); 180 181 for (unsigned i = 0; i < 256; i++) 182 { 183 if (keyBufferCopy[i] != KeyBuffer[i]) 184 { 185 if (mListener) 186 { 187 if (KeyBuffer[i]) 188 ret = mListener->keyPressed( KeyEvent( this, (KeyCode)i, _translateText((KeyCode)i) ) ); 189 else 190 ret = mListener->keyReleased( KeyEvent( this, (KeyCode)i, 0 ) ); 191 } 192 } 193 194 //If user returned false from callback, return immediately 195 if(ret == false) 196 return; 197 } 198 199 verifyAfterAltTab = false; 200 } 201 } 202 203 //--------------------------------------------------------------------------------------------------// 204 void Win32Keyboard::_read() 205 { 206 HRESULT hr = mKeyboard->GetDeviceState( sizeof(KeyBuffer), &KeyBuffer ); 207 208 if( hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED ) 209 { 210 hr = mKeyboard->Acquire(); 211 if (hr != DIERR_OTHERAPPHASPRIO) 212 mKeyboard->GetDeviceState(sizeof(KeyBuffer), &KeyBuffer); 213 } 214 215 //Set Shift, Ctrl, Alt 216 mModifiers = 0; 217 if( isKeyDown(KC_LCONTROL) || isKeyDown(KC_RCONTROL) ) 218 mModifiers |= Ctrl; 219 if( isKeyDown(KC_LSHIFT) || isKeyDown(KC_RSHIFT) ) 220 mModifiers |= Shift; 221 if( isKeyDown(KC_LMENU) || isKeyDown(KC_RMENU) ) 222 mModifiers |= Alt; 223 } 224 225 //--------------------------------------------------------------------------------------------------// 226 int Win32Keyboard::_translateText( KeyCode kc ) 227 { 228 if( mTextMode == Off ) 229 return 0; 230 231 BYTE keyState[256]; 232 HKL layout = GetKeyboardLayout(0); 233 if( GetKeyboardState(keyState) == 0 ) 234 return 0; 235 236 unsigned int vk = MapVirtualKeyEx(kc, 3, layout); 237 if( vk == 0 ) 238 return 0; 239 240 WCHAR buff[3] = {0}; 241 int ascii = ToUnicodeEx(vk, kc, keyState, buff, 3, 0, layout); 242 243 if(ascii == 1 && deadKey != '\0' ) 244 { 245 // A dead key is stored and we have just converted a character key 246 // Combine the two into a single character 247 WCHAR wcBuff[3] = {buff[0], deadKey, '\0'}; 248 WCHAR out[3]; 249 250 deadKey = '\0'; 251 if(FoldStringW(MAP_PRECOMPOSED, (LPWSTR)wcBuff, 3, (LPWSTR)out, 3)) 252 return out[0]; 253 } 254 else if (ascii == 1) 255 { // We have a single character 256 deadKey = '\0'; 257 return buff[0]; 258 } 259 else if(ascii == 2) 260 { // Convert a non-combining diacritical mark into a combining diacritical mark 261 // Combining versions range from 0x300 to 0x36F; only 5 (for French) have been mapped below 262 // http://www.fileformat.info/info/unicode/block/combining_diacritical_marks/images.htm 263 switch(buff[0]) { 264 case 0x5E: // Circumflex accent: 265 deadKey = 0x302; break; 266 case 0x60: // Grave accent: 267 deadKey = 0x300; break; 268 case 0xA8: // Diaeresis: 269 deadKey = 0x308; break; 270 case 0xB4: // Acute accent: 271 deadKey = 0x301; break; 272 case 0xB8: // Cedilla: 273 deadKey = 0x327; break; 274 default: 275 deadKey = buff[0]; break; 276 } 277 } 278 279 return 0; 280 } 281 282 //--------------------------------------------------------------------------------------------------// 283 bool Win32Keyboard::isKeyDown( KeyCode key ) const 284 { 285 return (KeyBuffer[key] & 0x80) != 0; 286 } 287 288 //--------------------------------------------------------------------------------------------------// 289 const std::string& Win32Keyboard::getAsString(KeyCode kc) 290 { 291 char temp[256]; 292 293 DIPROPSTRING prop; 294 prop.diph.dwSize = sizeof(DIPROPSTRING); 295 prop.diph.dwHeaderSize = sizeof(DIPROPHEADER); 296 prop.diph.dwObj = static_cast<DWORD>(kc); 297 prop.diph.dwHow = DIPH_BYOFFSET; 298 299 if (SUCCEEDED(mKeyboard->GetProperty(DIPROP_KEYNAME, &prop.diph))) 300 { 301 // convert the WCHAR in "wsz" to multibyte 302 if (WideCharToMultiByte(CP_ACP, 0, prop.wsz, -1, temp, sizeof(temp), NULL, NULL)) 303 return mGetString.assign(temp); 304 } 305 306 std::stringstream ss; 307 ss << "Key_" << (int)kc; 308 return mGetString.assign(ss.str()); 309 } 310 311 //--------------------------------------------------------------------------------------------------// 312 void Win32Keyboard::copyKeyStates( char keys[256] ) const 313 { 314 for(int i = 0; i < 256; ++i) 315 keys[i] = KeyBuffer[i] > 0; //Normalise the DX values (0x80) 316 } 317 318 //--------------------------------------------------------------------------------------------------// 319 void Win32Keyboard::setBuffered(bool buffered) 320 { 321 if( buffered != mBuffered ) 322 { 323 if(mKeyboard) 324 { 325 mKeyboard->Unacquire(); 326 mKeyboard->Release(); 327 mKeyboard = 0; 328 } 329 330 mBuffered = buffered; 331 _initialize(); 332 } 333 } 334