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 //--------------------------------------------------------------------------------------------------//
Win32Keyboard(InputManager * creator,IDirectInput8 * pDI,bool buffered,DWORD coopSettings)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 //--------------------------------------------------------------------------------------------------//
_initialize()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 //--------------------------------------------------------------------------------------------------//
~Win32Keyboard()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 //--------------------------------------------------------------------------------------------------//
capture()93 void Win32Keyboard::capture()
94 {
95 if( mBuffered )
96 _readBuffered();
97 else
98 _read();
99 }
100
101 //--------------------------------------------------------------------------------------------------//
_readBuffered()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 //--------------------------------------------------------------------------------------------------//
_read()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 //--------------------------------------------------------------------------------------------------//
_translateText(KeyCode kc)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 //--------------------------------------------------------------------------------------------------//
isKeyDown(KeyCode key) const283 bool Win32Keyboard::isKeyDown( KeyCode key ) const
284 {
285 return (KeyBuffer[key] & 0x80) != 0;
286 }
287
288 //--------------------------------------------------------------------------------------------------//
getAsString(KeyCode kc)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 //--------------------------------------------------------------------------------------------------//
copyKeyStates(char keys[256]) const312 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 //--------------------------------------------------------------------------------------------------//
setBuffered(bool buffered)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