Home | History | Annotate | Download | only in win32
      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