1 ////////////////////////////////////////////////////////////////////////////////////////
2 //
3 // Nestopia - NES/Famicom emulator written in C++
4 //
5 // Copyright (C) 2003-2008 Martin Freij
6 //
7 // This file is part of Nestopia.
8 //
9 // Nestopia is free software; you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation; either version 2 of the License, or
12 // (at your option) any later version.
13 //
14 // Nestopia is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with Nestopia; if not, write to the Free Software
21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 //
23 ////////////////////////////////////////////////////////////////////////////////////////
24 
25 #include <new>
26 #include "language/resource.h"
27 #include "NstApplicationException.hpp"
28 #include "NstApplicationInstance.hpp"
29 #include "NstSystemKeyboard.hpp"
30 #include "NstDirectInput.hpp"
31 #include "NstIoScreen.hpp"
32 #include "NstIoLog.hpp"
33 
34 #if NST_MSVC
35 #pragma comment(lib,"dinput8")
36 #pragma comment(lib,"dxguid")
37 #endif
38 
39 namespace Nestopia
40 {
41 	namespace DirectX
42 	{
43 		HeapString DirectInput::Keyboard::keyNames[MAX_KEYS];
44 
45 		struct DirectInput::Joystick::Lookup
46 		{
47 			uint (*code)(const void* const);
48 			ushort offset;
49 			ushort axis;
50 			wcstring name;
51 		};
52 
53 		const DirectInput::Joystick::Lookup DirectInput::Joystick::table[TABLE_KEYS] =
54 		{
55 			{ KeyPosDir,   DIJOFS_Y,         AXIS_Y,        L"+y"   },
56 			{ KeyPosDir,   DIJOFS_X,         AXIS_X,        L"+x"   },
57 			{ KeyNegDir,   DIJOFS_Y,         AXIS_Y,        L"-y"   },
58 			{ KeyNegDir,   DIJOFS_X,         AXIS_X,        L"-x"   },
59 			{ KeyPosDir,   DIJOFS_Z,         AXIS_Z,        L"+z"   },
60 			{ KeyNegDir,   DIJOFS_Z,         AXIS_Z,        L"-z"   },
61 			{ KeyPosDir,   DIJOFS_RY,        AXIS_RY,       L"+ry"  },
62 			{ KeyPosDir,   DIJOFS_RX,        AXIS_RX,       L"+rx"  },
63 			{ KeyPosDir,   DIJOFS_RY,        AXIS_RY,       L"-ry"  },
64 			{ KeyNegDir,   DIJOFS_RX,        AXIS_RX,       L"-rx"  },
65 			{ KeyPosDir,   DIJOFS_RZ,        AXIS_RZ,       L"+rz"  },
66 			{ KeyNegDir,   DIJOFS_RZ,        AXIS_RZ,       L"-rz"  },
67 			{ KeyNegDir,   DIJOFS_SLIDER(0), AXIS_SLIDER_0, L"-s0"  },
68 			{ KeyPosDir,   DIJOFS_SLIDER(0), AXIS_SLIDER_0, L"+s0"  },
69 			{ KeyNegDir,   DIJOFS_SLIDER(1), AXIS_SLIDER_1, L"-s1"  },
70 			{ KeyPosDir,   DIJOFS_SLIDER(1), AXIS_SLIDER_1, L"+s1"  },
71 			{ KeyPovUp,    DIJOFS_POV(0),    AXIS_POV_0,    L"-p0y" },
72 			{ KeyPovRight, DIJOFS_POV(0),    AXIS_POV_0,    L"+p0x" },
73 			{ KeyPovDown,  DIJOFS_POV(0),    AXIS_POV_0,    L"+p0y" },
74 			{ KeyPovLeft,  DIJOFS_POV(0),    AXIS_POV_0,    L"-p0x" },
75 			{ KeyPovUp,    DIJOFS_POV(1),    AXIS_POV_1,    L"-p1y" },
76 			{ KeyPovRight, DIJOFS_POV(1),    AXIS_POV_1,    L"+p1x" },
77 			{ KeyPovDown,  DIJOFS_POV(1),    AXIS_POV_1,    L"+p1y" },
78 			{ KeyPovLeft,  DIJOFS_POV(1),    AXIS_POV_1,    L"-p1x" },
79 			{ KeyPovUp,    DIJOFS_POV(2),    AXIS_POV_2,    L"-p2y" },
80 			{ KeyPovRight, DIJOFS_POV(2),    AXIS_POV_2,    L"+p2x" },
81 			{ KeyPovDown,  DIJOFS_POV(2),    AXIS_POV_2,    L"+p2y" },
82 			{ KeyPovLeft,  DIJOFS_POV(2),    AXIS_POV_2,    L"-p2x" },
83 			{ KeyPovUp,    DIJOFS_POV(3),    AXIS_POV_3,    L"-p3y" },
84 			{ KeyPovRight, DIJOFS_POV(3),    AXIS_POV_3,    L"+p3x" },
85 			{ KeyPovDown,  DIJOFS_POV(3),    AXIS_POV_3,    L"+p3y" },
86 			{ KeyPovLeft,  DIJOFS_POV(3),    AXIS_POV_3,    L"-p3x" }
87 		};
88 
89 		#ifdef NST_MSVC_OPTIMIZE
90 		#pragma optimize("t", on)
91 		#endif
92 
Fix(DIJOYSTATE & state) const93 		inline void DirectInput::Joystick::Calibrator::Fix(DIJOYSTATE& state) const
94 		{
95 			state.lX -= lX;
96 			state.lY -= lY;
97 			state.lZ -= lZ;
98 			state.lRx -= lRx;
99 			state.lRy -= lRy;
100 			state.lRz -= lRz;
101 		}
102 
Poll()103 		NST_FORCE_INLINE void DirectInput::Keyboard::Poll()
104 		{
105 			if (enabled)
106 			{
107 				HRESULT hResult;
108 
109 				if (FAILED(hResult=com.Poll()) || FAILED(hResult=com.GetDeviceState( Buffer::SIZE, buffer )))
110 					OnError( hResult );
111 			}
112 		}
113 
Poll()114 		NST_FORCE_INLINE void DirectInput::Joystick::Poll()
115 		{
116 			if (enabled)
117 			{
118 				HRESULT hResult;
119 
120 				if (SUCCEEDED(hResult=com.Poll()) && SUCCEEDED(hResult=com.GetDeviceState( sizeof(state), &state )))
121 					calibrator.Fix( state );
122 				else
123 					OnError( hResult );
124 			}
125 		}
126 
Poll()127 		void DirectInput::Poll()
128 		{
129 			keyboard.Poll();
130 
131 			for (Joysticks::Iterator it(joysticks.Begin()), end(joysticks.End()); it != end; ++it)
132 				it->Poll();
133 		}
134 
135 		#ifdef NST_MSVC_OPTIMIZE
136 		#pragma optimize("", on)
137 		#endif
138 
Base(HWND const h)139 		DirectInput::Base::Base(HWND const h)
140 		: com(Create()), hWnd(h) {}
141 
~Base()142 		DirectInput::Base::~Base()
143 		{
144 			com.Release();
145 		}
146 
Create()147 		IDirectInput8& DirectInput::Base::Create()
148 		{
149 			Io::Log() << "DirectInput: initializing..\r\n";
150 
151 			IDirectInput8* com;
152 
153 			if (FAILED(::DirectInput8Create( ::GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, reinterpret_cast<void**>(&com), NULL )))
154 				throw Application::Exception( IDS_ERR_FAILED, L"DirectInput8Create()" );
155 
156 			return *com;
157 		}
158 
DirectInput(HWND const hWnd)159 		DirectInput::DirectInput(HWND const hWnd)
160 		: base(hWnd), keyboard(base)
161 		{
162 			if (SUCCEEDED(base.com.EnumDevices( DI8DEVCLASS_GAMECTRL, EnumJoysticks, this, DIEDFL_ATTACHEDONLY )))
163 				Io::Log() << "DirectInput: found " << joysticks.Size() << " attached joystick(s)\r\n";
164 			else
165 				Io::Log() << "DirectInput: IDirectInput8::EnumDevices() failed! No joysticks can be used!\r\n";
166 		}
167 
~DirectInput()168 		DirectInput::~DirectInput()
169 		{
170 			for (uint i=joysticks.Size(); i; )
171 				joysticks[--i].Joystick::~Joystick();
172 		}
173 
EnumJoysticks(LPCDIDEVICEINSTANCE const instance,LPVOID const context)174 		BOOL CALLBACK DirectInput::EnumJoysticks(LPCDIDEVICEINSTANCE const instance,LPVOID const context)
175 		{
176 			if (instance)
177 			{
178 				DirectInput& directInput = *static_cast<DirectInput*>(context);
179 
180 				if (directInput.joysticks.Size() == MAX_JOYSTICKS)
181 				{
182 					Io::Log() << "DirectInput: warning, device count limit reached, stopping enumeration..\r\n";
183 					return DIENUM_STOP;
184 				}
185 
186 				Io::Log() << "DirectInput: enumerating device - name: "
187                           << (*instance->tszProductName ? instance->tszProductName : L"unknown")
188                           << ", GUID: "
189                           << System::Guid( instance->guidInstance ).GetString()
190                           << "\r\n";
191 
192 				directInput.joysticks.Grow();
193 
194 				try
195 				{
196 					new (&directInput.joysticks.Back()) Joystick( directInput.base, *instance );
197 				}
198 				catch (Joystick::Exception)
199 				{
200 					directInput.joysticks.Shrink();
201 					Io::Log() << "DirectInput: warning, bogus device, continuing enumeration..\r\n";
202 				}
203 			}
204 
205 			return DIENUM_CONTINUE;
206 		}
207 
Acquire()208 		void DirectInput::Acquire()
209 		{
210 			keyboard.Acquire();
211 
212 			for (Joysticks::Iterator it(joysticks.Begin()), end(joysticks.End()); it != end; ++it)
213 				it->Acquire();
214 		}
215 
Unacquire()216 		void DirectInput::Unacquire()
217 		{
218 			keyboard.Unacquire();
219 
220 			for (Joysticks::Iterator it(joysticks.Begin()), end(joysticks.End()); it != end; ++it)
221 				it->Unacquire();
222 		}
223 
Calibrate()224 		void DirectInput::Calibrate()
225 		{
226 			for (Joysticks::Iterator it(joysticks.Begin()), end(joysticks.End()); it != end; ++it)
227 				it->Calibrate();
228 		}
229 
BeginScanMode(HWND hWnd) const230 		void DirectInput::BeginScanMode(HWND hWnd) const
231 		{
232 			keyboard.BeginScanMode( hWnd );
233 
234 			for (Joysticks::ConstIterator it(joysticks.Begin()), end(joysticks.End()); it != end; ++it)
235 				it->BeginScanMode();
236 		}
237 
EndScanMode() const238 		void DirectInput::EndScanMode() const
239 		{
240 			keyboard.EndScanMode();
241 
242 			for (Joysticks::ConstIterator it(joysticks.Begin()), end(joysticks.End()); it != end; ++it)
243 				it->EndScanMode();
244 		}
245 
Build(const Key * const keys,const uint count)246 		void DirectInput::Build(const Key* const keys,const uint count)
247 		{
248 			keyboard.Enable( false );
249 
250 			for (const Key *it=keys, *const end=keys+count; it != end; ++it)
251 			{
252 				if (keyboard.Assigned( *it ))
253 				{
254 					keyboard.Enable( true );
255 					break;
256 				}
257 			}
258 
259 			for (Joysticks::Iterator it(joysticks.Begin()), end(joysticks.End()); it != end; ++it)
260 			{
261 				it->Enable( false );
262 
263 				for (const Key *jt=keys, *const jend=keys+count; jt != jend; ++jt)
264 				{
265 					if (it->Assigned( *jt ))
266 					{
267 						it->Enable( true );
268 						break;
269 					}
270 				}
271 			}
272 		}
273 
ScanKey(Key & key,const ScanMode scanMode)274 		DirectInput::ScanResult DirectInput::ScanKey(Key& key,const ScanMode scanMode)
275 		{
276 			const ScanResult scan = (scanMode == SCAN_MODE_ALL ? keyboard.Scan( key ) : SCAN_NO_KEY);
277 
278 			if (scan != SCAN_GOOD_KEY)
279 			{
280 				if (scan == SCAN_NO_KEY)
281 				{
282 					for (Joysticks::Iterator it(joysticks.Begin()), end(joysticks.End()); it != end; ++it)
283 					{
284 						if (it->Scan( key ))
285 							return SCAN_GOOD_KEY;
286 					}
287 				}
288 
289 				key.Unmap();
290 			}
291 
292 			return scan;
293 		}
294 
MapKey(Key & key,wcstring const name,const System::Guid * const guids,const uint numGuids) const295 		bool DirectInput::MapKey(Key& key,wcstring const name,const System::Guid* const guids,const uint numGuids) const
296 		{
297 			key.Unmap();
298 
299 			if
300 			(
301 				name && *name &&
302 				(name[0] != '.' || name[1] != '.' || name[2] != '.' || name[3] != '\0')
303 			)
304 			{
305 				if
306 				(
307 					(name[0] == '(') &&
308 					(name[1] == 'j' || name[1] == 'J') &&
309 					(name[2] == 'o' || name[2] == 'O') &&
310 					(name[3] == 'y' || name[3] == 'Y') &&
311 					(name[4] == ' ') &&
312 					(name[5] >= '0' && name[5] <= '9') &&
313 					(
314 						(name[6] == ')' && name[7] == ' ') ||
315 						(name[6] >= '0' && name[6] <= '9' && name[7] == ')' && name[8] == ' ')
316 					)
317 				)
318 				{
319 					uint index = name[5] - '0';
320 
321 					if (name[6] != ')')
322 						index = (index * 10) + (name[6] - '0');
323 
324 					if (index < NST_MIN(MAX_JOYSTICKS,numGuids))
325 					{
326 						const System::Guid& guid = guids[index];
327 
328 						for (Joysticks::ConstIterator it(joysticks.Begin()), end(joysticks.End()); it != end; ++it)
329 						{
330 							if (it->GetGuid() == guid)
331 								return it->Map( key, name + (name[7] == ' ' ? 8 : 9) );
332 						}
333 					}
334 				}
335 				else
336 				{
337 					return keyboard.Map( key, name );
338 				}
339 			}
340 
341 			return false;
342 		}
343 
GetKeyName(const Key & key) const344 		const HeapString DirectInput::GetKeyName(const Key& key) const
345 		{
346 			if (key.Assigned())
347 			{
348 				if (keyboard.Assigned( key ))
349 				{
350 					return keyboard.GetName( key );
351 				}
352 				else
353 				{
354 					for (uint i=0, n=joysticks.Size(); i < n; ++i)
355 					{
356 						if (joysticks[i].Assigned( key ))
357 							return HeapString("(joy ") << i << ") " << joysticks[i].GetName( key );
358 					}
359 
360 					HeapString name;
361 
362 					if (key.vKey & FCONTROL)
363 						name << System::Keyboard::GetName( VK_CONTROL ) << '+';
364 
365 					if (key.vKey & FALT)
366 						name << System::Keyboard::GetName( VK_MENU ) << '+';
367 
368 					if (key.vKey & FSHIFT)
369 						name << System::Keyboard::GetName( VK_SHIFT ) << '+';
370 
371 					name << System::Keyboard::GetName( key.vKey >> 8 );
372 
373 					return name;
374 				}
375 			}
376 
377 			return L"...";
378 		}
379 
Device(IDirectInputDevice8 & c)380 		DirectInput::Device::Device(IDirectInputDevice8& c)
381 		: com(c), enabled(false) {}
382 
~Device()383 		DirectInput::Device::~Device()
384 		{
385 			com.Unacquire();
386 			com.Release();
387 		}
388 
Enable(bool enable)389 		void DirectInput::Device::Enable(bool enable)
390 		{
391 			enabled = enable;
392 		}
393 
Acquire(void * const data,const uint size)394 		bool DirectInput::Device::Acquire(void* const data,const uint size)
395 		{
396 			return enabled && SUCCEEDED(com.Acquire()) && SUCCEEDED(com.Poll()) && SUCCEEDED(com.GetDeviceState( size, data ));
397 		}
398 
Unacquire()399 		void DirectInput::Device::Unacquire()
400 		{
401 			com.Unacquire();
402 		}
403 
Keyboard(Base & base)404 		DirectInput::Keyboard::Keyboard(Base& base)
405 		: Device(Create(base.com)), hWnd(base.hWnd)
406 		{
407 			for (uint i=0; i < MAX_KEYS; ++i)
408 				keyNames[i] << i;
409 
410 			com.EnumObjects( EnumObjects, NULL, DIDFT_BUTTON );
411 			SetCooperativeLevel( base.hWnd, COOPERATIVE_FLAGS );
412 			Clear();
413 		}
414 
Create(IDirectInput8 & base)415 		IDirectInputDevice8& DirectInput::Keyboard::Create(IDirectInput8& base)
416 		{
417 			IDirectInputDevice8* com;
418 
419 			if (FAILED(base.CreateDevice( GUID_SysKeyboard, &com, NULL )))
420 				throw Application::Exception( IDS_ERR_FAILED, L"IDirectInput8::CreateDevice()" );
421 
422 			if (FAILED(com->SetDataFormat( &c_dfDIKeyboard )))
423 			{
424 				com->Release();
425 				throw Application::Exception( IDS_ERR_FAILED, L"IDirectInputDevice8::SetDataFormat()" );
426 			}
427 
428 			return *com;
429 		}
430 
EnumObjects(LPCDIDEVICEOBJECTINSTANCE const obj,LPVOID)431 		BOOL CALLBACK DirectInput::Keyboard::EnumObjects(LPCDIDEVICEOBJECTINSTANCE const obj,LPVOID)
432 		{
433 			NST_VERIFY( obj->dwOfs < MAX_KEYS && *obj->tszName );
434 
435 			if (obj->dwOfs < MAX_KEYS && *obj->tszName)
436 			{
437 				HeapString& string = keyNames[obj->dwOfs];
438 				string = obj->tszName;
439 
440 				::CharUpperBuff( string.Ptr(), 1 );
441 
442 				if (string.Length() > 1)
443 					::CharLowerBuff( string.Ptr() + 1, string.Length() - 1 );
444 			}
445 
446 			return DIENUM_CONTINUE;
447 		}
448 
SetCooperativeLevel(HWND const hWnd,const DWORD flags) const449 		void DirectInput::Keyboard::SetCooperativeLevel(HWND const hWnd,const DWORD flags) const
450 		{
451 			if (FAILED(com.SetCooperativeLevel( hWnd, flags )))
452 				throw Application::Exception( IDS_ERR_FAILED, L"IDirectInputDevice8::SetCooperativeLevel()" );
453 		}
454 
Map(Key & key,wcstring name) const455 		bool DirectInput::Keyboard::Map(Key& key,wcstring name) const
456 		{
457 			for (uint i=0; i < MAX_KEYS; ++i)
458 			{
459 				if (keyNames[i] == name)
460 					return Map( key, i );
461 			}
462 
463 			return false;
464 		}
465 
Map(Key & key,const uint code) const466 		bool DirectInput::Keyboard::Map(Key& key,const uint code) const
467 		{
468 			if (code && code <= 0xFF && code != DIK_LWIN)
469 			{
470 				key.data = buffer + code;
471 				key.code = KeyDown;
472 				return true;
473 			}
474 
475 			return false;
476 		}
477 
BeginScanMode(HWND hWndScan) const478 		void DirectInput::Keyboard::BeginScanMode(HWND hWndScan) const
479 		{
480 			com.Unacquire();
481 			SetCooperativeLevel( hWndScan, SCAN_COOPERATIVE_FLAGS );
482 			com.Acquire();
483 		}
484 
EndScanMode() const485 		void DirectInput::Keyboard::EndScanMode() const
486 		{
487 			com.Unacquire();
488 			SetCooperativeLevel( hWnd, COOPERATIVE_FLAGS );
489 		}
490 
Scan(uchar (& data)[MAX_KEYS]) const491 		bool DirectInput::Keyboard::Scan(uchar (&data)[MAX_KEYS]) const
492 		{
493 			if (SUCCEEDED(com.Poll()) && SUCCEEDED(com.GetDeviceState( MAX_KEYS, data )))
494 				return true;
495 
496 			std::memset( data, 0, MAX_KEYS );
497 
498 			return false;
499 		}
500 
Scan(Key & key) const501 		DirectInput::ScanResult DirectInput::Keyboard::Scan(Key& key) const
502 		{
503 			uchar data[MAX_KEYS];
504 
505 			if (Scan( data ))
506 			{
507 				for (uint i=0; i < MAX_KEYS; ++i)
508 				{
509 					if (data[i] & 0x80U)
510 					{
511 						switch (i)
512 						{
513 							case DIK_CAPITAL:
514 							case DIK_NUMLOCK:
515 							case DIK_SCROLL:
516 							case DIK_KANA:
517 							case DIK_KANJI:
518 								continue;
519 
520 							default:
521 								return Map( key, i ) ? SCAN_GOOD_KEY : SCAN_INVALID_KEY;
522 						}
523 					}
524 				}
525 			}
526 
527 			return SCAN_NO_KEY;
528 		}
529 
OnError(const HRESULT hResult)530 		void DirectInput::Keyboard::OnError(const HRESULT hResult)
531 		{
532 			NST_ASSERT( FAILED(hResult) );
533 
534 			switch (hResult)
535 			{
536 				case DIERR_INPUTLOST:
537 				case DIERR_NOTACQUIRED:
538 
539 					if (::GetActiveWindow() == hWnd)
540 					{
541 						Acquire();
542 						break;
543 					}
544 
545 				default:
546 
547 					Clear();
548 					break;
549 			}
550 		}
551 
Assigned(const Key & key) const552 		bool DirectInput::Keyboard::Assigned(const Key& key) const
553 		{
554 			return key.data >= buffer && key.data < (buffer + Buffer::SIZE);
555 		}
556 
GetName(const Key & key) const557 		wcstring DirectInput::Keyboard::GetName(const Key& key) const
558 		{
559 			NST_VERIFY( Assigned(key) );
560 			return keyNames[key.data - buffer].Ptr();
561 		}
562 
Clear()563 		void DirectInput::Keyboard::Clear()
564 		{
565 			std::memset( buffer, 0, Buffer::SIZE );
566 		}
567 
Acquire()568 		void DirectInput::Keyboard::Acquire()
569 		{
570 			if (!Device::Acquire( buffer, Buffer::SIZE ))
571 				Clear();
572 		}
573 
Unacquire()574 		void DirectInput::Keyboard::Unacquire()
575 		{
576 			Device::Unacquire();
577 			Clear();
578 		}
579 
Joystick(Base & base,const DIDEVICEINSTANCE & instance)580 		DirectInput::Joystick::Joystick(Base& base,const DIDEVICEINSTANCE& instance)
581 		:
582 		Device      (Create(base,instance.guidInstance)),
583 		caps        (com,instance),
584 		calibrated  (false),
585 		scanEnabled (true),
586 		deadZone    (UINT_MAX),
587 		axes        (DEFAULT_AXES)
588 		{
589 			SetAxisDeadZone( DEFAULT_DEADZONE );
590 
591 			if (SUCCEEDED(com.Acquire()))
592 			{
593 				if (SUCCEEDED(com.Poll()))
594 					com.GetDeviceState( sizeof(state), &state );
595 
596 				com.Unacquire();
597 			}
598 		}
599 
Calibrator()600 		DirectInput::Joystick::Calibrator::Calibrator()
601 		:
602 		lX  (0),
603 		lY  (0),
604 		lZ  (0),
605 		lRx (0),
606 		lRy (0),
607 		lRz (0)
608 		{}
609 
Context(Caps & c,IDirectInputDevice8 & d)610 		DirectInput::Joystick::Caps::Context::Context(Caps& c,IDirectInputDevice8& d)
611 		: caps(c), com(d), numButtons(0) {}
612 
Caps(IDirectInputDevice8 & com,const DIDEVICEINSTANCE & instance)613 		DirectInput::Joystick::Caps::Caps(IDirectInputDevice8& com,const DIDEVICEINSTANCE& instance)
614 		: axes(0), guid(instance.guidInstance), name(*instance.tszProductName ? instance.tszProductName : L"unknown")
615 		{
616 			Context context( *this, com );
617 
618 			if (FAILED(com.EnumObjects( EnumObjectsProc, &context, DIDFT_ALL )) || !(context.numButtons|axes))
619 				throw ERR_API;
620 		}
621 
Create(Base & base,const GUID & guid)622 		IDirectInputDevice8& DirectInput::Joystick::Create(Base& base,const GUID& guid)
623 		{
624 			IDirectInputDevice8* com;
625 
626 			if (FAILED(base.com.CreateDevice( guid, &com, NULL )))
627 				throw ERR_API;
628 
629 			if
630 			(
631 				FAILED(com->SetDataFormat( &c_dfDIJoystick )) ||
632 				FAILED(com->SetCooperativeLevel( base.hWnd, DISCL_BACKGROUND|DISCL_NONEXCLUSIVE ))
633 			)
634 			{
635 				com->Release();
636 				throw ERR_API;
637 			}
638 
639 			return *com;
640 		}
641 
EnumObjectsProc(LPCDIDEVICEOBJECTINSTANCE const info,LPVOID const ref)642 		BOOL CALLBACK DirectInput::Joystick::Caps::EnumObjectsProc(LPCDIDEVICEOBJECTINSTANCE const info,LPVOID const ref)
643 		{
644 			Context& context = *static_cast<Context*>(ref);
645 
646 			if (info->guidType == GUID_Button)
647 			{
648 				++context.numButtons;
649 			}
650 			else
651 			{
652 				uint flag;
653 
654                      if ( info->guidType == GUID_XAxis  ) flag = AXIS_X;
655 				else if ( info->guidType == GUID_YAxis  ) flag = AXIS_Y;
656 				else if ( info->guidType == GUID_ZAxis  ) flag = AXIS_Z;
657 				else if ( info->guidType == GUID_RxAxis ) flag = AXIS_RX;
658 				else if ( info->guidType == GUID_RyAxis ) flag = AXIS_RY;
659 				else if ( info->guidType == GUID_RzAxis ) flag = AXIS_RZ;
660 				else if ( info->guidType == GUID_POV    )
661 				{
662 					if (context.caps.axes & AXIS_POV_3)
663 					{
664 						return DIENUM_CONTINUE;
665 					}
666 					else if (context.caps.axes & AXIS_POV_2)
667 					{
668 						flag = AXIS_POV_3;
669 					}
670 					else if (context.caps.axes & AXIS_POV_1)
671 					{
672 						flag = AXIS_POV_2;
673 					}
674 					else if (context.caps.axes & AXIS_POV_0)
675 					{
676 						flag = AXIS_POV_1;
677 					}
678 					else
679 					{
680 						flag = AXIS_POV_0;
681 					}
682 				}
683 				else if ( info->guidType == GUID_Slider )
684 				{
685 					if (context.caps.axes & AXIS_SLIDER_1)
686 					{
687 						return DIENUM_CONTINUE;
688 					}
689 					else if (context.caps.axes & AXIS_SLIDER_0)
690 					{
691 						flag = AXIS_SLIDER_1;
692 					}
693 					else
694 					{
695 						flag = AXIS_SLIDER_0;
696 					}
697 				}
698 				else
699 				{
700 					return DIENUM_CONTINUE;
701 				}
702 
703 				if (info->dwType & DIDFT_AXIS)
704 				{
705 					Object::Pod<DIPROPRANGE> diprg;
706 
707 					diprg.diph.dwSize       = sizeof(diprg);
708 					diprg.diph.dwHeaderSize = sizeof(diprg.diph);
709 					diprg.diph.dwHow        = DIPH_BYID;
710 					diprg.diph.dwObj        = info->dwType;
711 					diprg.lMin              = AXIS_MIN_RANGE;
712 					diprg.lMax              = AXIS_MAX_RANGE;
713 
714 					if (FAILED(context.com.SetProperty( DIPROP_RANGE, &diprg.diph )))
715 					{
716 						if (FAILED(context.com.GetProperty( DIPROP_RANGE, &diprg.diph )) || diprg.lMin >= 0 || diprg.lMax <= 0)
717 						{
718 							Io::Log() << "DirectInput: warning, SetProperty(DIPROP_RANGE) failed, skipping axis..\r\n";
719 							return DIENUM_CONTINUE;
720 						}
721 					}
722 				}
723 
724 				context.caps.axes |= flag;
725 			}
726 
727 			return DIENUM_CONTINUE;
728 		}
729 
SetAxisDeadZone(const uint value)730 		bool DirectInput::Joystick::SetAxisDeadZone(const uint value)
731 		{
732 			NST_ASSERT( value <= DEADZONE_MAX );
733 
734 			if (deadZone != value)
735 			{
736 				deadZone = value;
737 
738 				Object::Pod<DIPROPDWORD> diprd;
739 
740 				diprd.diph.dwSize       = sizeof(diprd);
741 				diprd.diph.dwHeaderSize = sizeof(diprd.diph);
742 				diprd.diph.dwHow        = DIPH_DEVICE;
743 				diprd.dwData            = value * 100;
744 
745 				com.SetProperty( DIPROP_DEADZONE, &diprd.diph );
746 				return true;
747 			}
748 
749 			return false;
750 		}
751 
Clear()752 		void DirectInput::Joystick::Clear()
753 		{
754 			state.Clear();
755 			state.rgdwPOV[3] = state.rgdwPOV[2] = state.rgdwPOV[1] = state.rgdwPOV[0] = DWORD(~0UL);
756 		}
757 
Acquire()758 		void DirectInput::Joystick::Acquire()
759 		{
760 			if (Device::Acquire( &state, sizeof(state) ))
761 			{
762 				if (!calibrated)
763 				{
764 					calibrated = true;
765 					calibrator.Reset( state, false );
766 				}
767 
768 				calibrator.Fix( state );
769 			}
770 			else
771 			{
772 				Clear();
773 			}
774 		}
775 
Unacquire()776 		void DirectInput::Joystick::Unacquire()
777 		{
778 			Device::Unacquire();
779 			Clear();
780 		}
781 
Reset(DIJOYSTATE & state,bool full)782 		inline void DirectInput::Joystick::Calibrator::Reset(DIJOYSTATE& state,bool full)
783 		{
784 			lX = full ? state.lX : 0;
785 			lY = full ? state.lY : 0;
786 			lZ = state.lZ;
787 			lRx = state.lRx;
788 			lRy = state.lRy;
789 			lRz = state.lRz;
790 		}
791 
Calibrate()792 		void DirectInput::Joystick::Calibrate()
793 		{
794 			if (SUCCEEDED(com.Acquire()))
795 			{
796 				Object::Pod<DIJOYSTATE> tmp;
797 
798 				if (SUCCEEDED(com.Poll()) && SUCCEEDED(com.GetDeviceState( sizeof(tmp), &tmp )))
799 				{
800 					calibrated = true;
801 					calibrator.Reset( tmp, true );
802 				}
803 
804 				com.Unacquire();
805 			}
806 		}
807 
BeginScanMode() const808 		void DirectInput::Joystick::BeginScanMode() const
809 		{
810 			com.Acquire();
811 		}
812 
EndScanMode() const813 		void DirectInput::Joystick::EndScanMode() const
814 		{
815 			com.Unacquire();
816 		}
817 
Scan(Key & key)818 		bool DirectInput::Joystick::Scan(Key& key)
819 		{
820 			DIJOYSTATE tmp;
821 
822 			if (scanEnabled && SUCCEEDED(com.Poll()) && SUCCEEDED(com.GetDeviceState( sizeof(tmp), &tmp )))
823 			{
824 				if (!calibrated)
825 				{
826 					calibrated = true;
827 					calibrator.Reset( tmp, false );
828 				}
829 
830 				calibrator.Fix( tmp );
831 
832 				for (uint i=0; i < Caps::NUM_BUTTONS; ++i)
833 				{
834 					if (tmp.rgbButtons[i] & 0x80U)
835 					{
836 						key.data = state.rgbButtons + i;
837 						key.code = KeyDown;
838 						return true;
839 					}
840 				}
841 
842 				for (uint i=0; i < TABLE_KEYS; ++i)
843 				{
844 					if (caps.axes & axes & table[i].axis)
845 					{
846 						key.data = reinterpret_cast<const BYTE*>(&tmp) + table[i].offset;
847 						key.code = table[i].code;
848 
849 						if (key.GetState())
850 						{
851 							key.data = reinterpret_cast<const BYTE*>(&state) + table[i].offset;
852 							return true;
853 						}
854 					}
855 				}
856 			}
857 
858 			return false;
859 		}
860 
Map(Key & key,wcstring const name) const861 		bool DirectInput::Joystick::Map(Key& key,wcstring const name) const
862 		{
863 			if (*name)
864 			{
865 				if (name[0] >= '0' && name[0] <= '9')
866 				{
867 					uint index = name[0] - '0';
868 
869 					if (name[1] >= '0' && name[1] <= '9')
870 						index = (index * 10) + (name[1] - '0');
871 
872 					if (index < Caps::NUM_BUTTONS)
873 					{
874 						key.data = state.rgbButtons + index;
875 						key.code = KeyDown;
876 						return true;
877 					}
878 				}
879 				else
880 				{
881 					const GenericString axis( name );
882 
883 					for (uint i=TABLE_KEYS; i; )
884 					{
885 						if (caps.axes & table[--i].axis)
886 						{
887 							if (axis == table[i].name)
888 							{
889 								key.data = reinterpret_cast<const BYTE*>(&state) + table[i].offset;
890 								key.code = table[i].code;
891 								return true;
892 							}
893 						}
894 					}
895 				}
896 			}
897 
898 			return false;
899 		}
900 
Assigned(const Key & key) const901 		bool DirectInput::Joystick::Assigned(const Key& key) const
902 		{
903 			return
904 			(
905 				key.data >= reinterpret_cast<const BYTE*>(&state) &&
906 				key.data <  reinterpret_cast<const BYTE*>(&state) + sizeof(state)
907 			);
908 		}
909 
GetName(const Key & key) const910 		wcstring DirectInput::Joystick::GetName(const Key& key) const
911 		{
912 			NST_VERIFY( Assigned(key) );
913 
914 			if (key.code == KeyDown)
915 			{
916 				static wchar_t button[] = L"xx";
917 
918 				const uint index = key.data - state.rgbButtons;
919 				button[0] = index < 10 ? '0' + index : '0' + index / 10;
920 				button[1] = index < 10 ? '\0'        : '0' + index % 10;
921 
922 				return button;
923 			}
924 
925 			for (const Lookup* it = table; ; ++it)
926 			{
927 				if (key.data == reinterpret_cast<const BYTE*>(&state) + it->offset && key.code == it->code)
928 					return it->name;
929 			}
930 		}
931 
OnError(const HRESULT hResult)932 		void DirectInput::Joystick::OnError(const HRESULT hResult)
933 		{
934 			NST_ASSERT( FAILED(hResult) );
935 
936 			switch (hResult)
937 			{
938 				case DIERR_INPUTLOST:
939 				case DIERR_NOTACQUIRED:
940 
941 					Acquire();
942 					break;
943 
944 				case DIERR_UNPLUGGED:
945 
946 					Enable( false );
947 					Io::Screen() << L"Error! Joystick unplugged!";
948 
949 				default:
950 
951 					Clear();
952 					break;
953 			}
954 		}
955 
956 		#ifdef NST_MSVC_OPTIMIZE
957 		#pragma optimize("t", on)
958 		#endif
959 
KeyDown(const void * const data)960 		uint DirectInput::KeyDown(const void* const data)
961 		{
962 			return 0U - (*static_cast<const BYTE*>(data) >> 7);
963 		}
964 
KeyPosDir(const void * const data)965 		uint DirectInput::KeyPosDir(const void* const data)
966 		{
967 			if (LONG_MIN >> (sizeof(long) * CHAR_BIT - 1) == ~0UL)
968 				return (-*static_cast<const long*>(data) >> (sizeof(long) * CHAR_BIT - 1));
969 			else
970 				return (*static_cast<const long*>(data) > 0) ? ~0U : 0U;
971 		}
972 
KeyNegDir(const void * const data)973 		uint DirectInput::KeyNegDir(const void* const data)
974 		{
975 			if (LONG_MIN >> (sizeof(long) * CHAR_BIT - 1) == ~0UL)
976 				return (*static_cast<const long*>(data) >> (sizeof(long) * CHAR_BIT - 1));
977 			else
978 				return (*static_cast<const long*>(data) < 0) ? ~0U : 0U;
979 		}
980 
KeyPovUp(const void * const data)981 		uint DirectInput::KeyPovUp(const void* const data)
982 		{
983 			const DWORD pov = *static_cast<const DWORD*>(data);
984 
985 			return
986 			(
987 				(pov & Joystick::POV_CENTER) != Joystick::POV_CENTER &&
988 				(pov >= Joystick::POV_UPLEFT || pov <= Joystick::POV_UPRIGHT)
989 			)   ? ~0U : 0U;
990 		}
991 
KeyPovRight(const void * const data)992 		uint DirectInput::KeyPovRight(const void* const data)
993 		{
994 			const DWORD pov = *static_cast<const DWORD*>(data);
995 
996 			return
997 			(
998 				(pov & Joystick::POV_CENTER) != Joystick::POV_CENTER &&
999 				(pov >= Joystick::POV_UPRIGHT && pov <= Joystick::POV_DOWNRIGHT)
1000 			)   ? ~0U : 0U;
1001 		}
1002 
KeyPovDown(const void * const data)1003 		uint DirectInput::KeyPovDown(const void* const data)
1004 		{
1005 			const DWORD pov = *static_cast<const DWORD*>(data);
1006 
1007 			return
1008 			(
1009 				(pov & Joystick::POV_CENTER) != Joystick::POV_CENTER &&
1010 				(pov >= Joystick::POV_DOWNRIGHT && pov <= Joystick::POV_DOWNLEFT)
1011 			)   ? ~0U : 0U;
1012 		}
1013 
KeyPovLeft(const void * const data)1014 		uint DirectInput::KeyPovLeft(const void* const data)
1015 		{
1016 			const DWORD pov = *static_cast<const DWORD*>(data);
1017 
1018 			return
1019 			(
1020 				(pov & Joystick::POV_CENTER) != Joystick::POV_CENTER &&
1021 				(pov >= Joystick::POV_DOWNLEFT && pov <= Joystick::POV_UPLEFT)
1022 			)   ? ~0U : 0U;
1023 		}
1024 
KeyNone(const void * const)1025 		uint DirectInput::KeyNone(const void* const)
1026 		{
1027 			return 0;
1028 		}
1029 
GetToggle(bool & prev) const1030 		bool DirectInput::Key::GetToggle(bool& prev) const
1031 		{
1032 			if (GetState())
1033 			{
1034 				if (!prev)
1035 				{
1036 					prev = true;
1037 					return true;
1038 				}
1039 			}
1040 			else
1041 			{
1042 				prev = false;
1043 			}
1044 
1045 			return false;
1046 		}
1047 
1048 		#ifdef NST_MSVC_OPTIMIZE
1049 		#pragma optimize("", on)
1050 		#endif
1051 
MapVirtualKey(const uint code,const uint vk1,const uint vk2,const uint vk3)1052 		bool DirectInput::Key::MapVirtualKey(const uint code,const uint vk1,const uint vk2,const uint vk3)
1053 		{
1054 			Unmap();
1055 
1056 			if (!code || code > 0xFF)
1057 				return false;
1058 
1059 			vKey = code << 8;
1060 
1061 			if (vk1 == VK_MENU || vk2 == VK_MENU || vk3 == VK_MENU)
1062 				vKey |= FALT;
1063 
1064 			if (vk1 == VK_SHIFT || vk2 == VK_SHIFT || vk3 == VK_SHIFT)
1065 				vKey |= FSHIFT;
1066 
1067 			if (vk1 == VK_CONTROL || vk2 == VK_CONTROL || vk3 == VK_CONTROL)
1068 				vKey |= FCONTROL;
1069 
1070 			// forbidden keys:
1071 			//
1072 			// ALT+F4
1073 			// ALT+F6
1074 			// ALT+TAB
1075 			// ALT+SPACE
1076 			// ALT+ESC
1077 			// CTRL+F4
1078 			// CTRL+ESC
1079 			// CTRL+ALT+DELETE
1080 			// LWIN
1081 
1082 			switch (code)
1083 			{
1084 				case VK_F4:
1085 
1086 					if (vKey & FCONTROL)
1087 						return false;
1088 
1089 				case VK_F6:
1090 				case VK_TAB:
1091 				case VK_SPACE:
1092 
1093 					if (vKey & FALT)
1094 						return false;
1095 
1096 					break;
1097 
1098 				case VK_ESCAPE:
1099 
1100 					if (vKey & (FCONTROL|FALT))
1101 						return false;
1102 
1103 					break;
1104 
1105 				case VK_DELETE:
1106 
1107 					if ((vKey & (FCONTROL|FALT)) == (FCONTROL|FALT))
1108 						return false;
1109 
1110 					break;
1111 
1112 				case VK_LWIN:
1113 					return false;
1114 			}
1115 
1116 			return true;
1117 		}
1118 
MapVirtualKey(GenericString name)1119 		bool DirectInput::Key::MapVirtualKey(GenericString name)
1120 		{
1121 			Unmap();
1122 
1123 			if (name.Empty())
1124 				return false;
1125 
1126 			const GenericString vkNames[3] =
1127 			{
1128 				System::Keyboard::GetName( VK_CONTROL ),
1129 				System::Keyboard::GetName( VK_MENU ),
1130 				System::Keyboard::GetName( VK_SHIFT )
1131 			};
1132 
1133 			uint vk[3] = {0,0,0};
1134 
1135 			for (uint i=0; i < 3; ++i)
1136 			{
1137 				for (uint j=0; j < 3; ++j)
1138 				{
1139 					if (!vk[j] && name.Length() > vkNames[j].Length() && name(0,vkNames[j].Length()) == vkNames[j] && name[vkNames[j].Length()] == '+')
1140 					{
1141 						vk[j] = (j==0 ? VK_CONTROL : j==1 ? VK_MENU : VK_SHIFT);
1142 						name = name( vkNames[j].Length() + 1 );
1143 						break;
1144 					}
1145 				}
1146 			}
1147 
1148 			return MapVirtualKey( System::Keyboard::GetKey( name ), vk[0], vk[1], vk[2] );
1149 		}
1150 
IsVirtualKey() const1151 		bool DirectInput::Key::IsVirtualKey() const
1152 		{
1153 			return (code == KeyNone && data);
1154 		}
1155 
GetVirtualKey(ACCEL & accel) const1156 		bool DirectInput::Key::GetVirtualKey(ACCEL& accel) const
1157 		{
1158 			if (code == KeyNone && data)
1159 			{
1160 				accel.fVirt = (vKey & 0xFF) | FVIRTKEY;
1161 				accel.key = vKey >> 8;
1162 				return true;
1163 			}
1164 
1165 			accel.fVirt = 0;
1166 			accel.key = 0;
1167 			return false;
1168 		}
1169 	}
1170 }
1171