Lib
QOLを高める
input.cpp
Go to the documentation of this file.
1 #include "stdafx.h"
2 #include "include/input.h"
3 #include "include/debug.h"
4 #include "include/exceptions.h"
5 
6 #pragma comment(lib, "dinput8.lib")
7 #pragma comment(lib, "dxguid.lib")
8 
9 namespace yappy {
10 namespace input {
11 
13 using error::DIError;
14 
15 DInput::DInput(HINSTANCE hInst, HWND hWnd, bool foreground, bool exclusive)
16 {
17  m_key.fill(false);
18 
19  debug::writeLine(L"Initializing DirectInput...");
20 
21  HRESULT hr = S_OK;
22 
23  // Initialize DirectInput
24  IDirectInput8 *ptmpDi;
25  hr = ::DirectInput8Create(hInst, DIRECTINPUT_VERSION, IID_IDirectInput8,
26  reinterpret_cast<void **>(&ptmpDi), NULL);
27  checkDXResult<DIError>(hr, "DirectInput8Create() failed");
28  m_pDi.reset(ptmpDi);
29 
30  // Key Device
31  debug::writeLine(L"Get Keyboard...");
32  IDirectInputDevice8 *ptmpKeyDevice;
33  hr = m_pDi->CreateDevice(GUID_SysKeyboard, &ptmpKeyDevice, NULL);
34  checkDXResult<DIError>(hr, "IDirectInput8::CreateDevice() failed");
35  m_pKeyDevice.reset(ptmpKeyDevice);
36 
37  hr = m_pKeyDevice->SetDataFormat(&c_dfDIKeyboard);
38  checkDXResult<DIError>(hr, "IDirectInputDevice8::SetDataFormat() failed");
39  DWORD coopLevel = DISCL_NOWINKEY;
40  coopLevel |= (foreground ? DISCL_FOREGROUND : DISCL_BACKGROUND);
41  coopLevel |= (exclusive ? DISCL_EXCLUSIVE : DISCL_NONEXCLUSIVE);
42  hr = m_pKeyDevice->SetCooperativeLevel(hWnd, coopLevel);
43  checkDXResult<DIError>(hr, "IDirectInputDevice8::SetCooperativeLevel() failed");
44  debug::writeLine(L"Get Keyboard OK");
45 
46  // GamePad Device
47  updateControllers(hWnd, foreground, exclusive);
48 
49  debug::writeLine(L"Initializing DirectInput OK");
50 }
51 
53 {
54  // Unacquire (ignore errors)
55  m_pKeyDevice->Unacquire();
56  for (auto &dev : m_pPadDevs) {
57  dev->Unacquire();
58  }
59  debug::writeLine(L"Finalize DirectInput");
60 }
61 
62 // file local functions for callback
63 namespace {
64 
65 BOOL CALLBACK enumDevicesCallback(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef)
66 {
67  std::vector<DIDEVICEINSTANCE> *devs = reinterpret_cast<decltype(devs)>(pvRef);
68  devs->emplace_back(*lpddi);
69  return DIENUM_CONTINUE;
70 }
71 
72 BOOL CALLBACK enumAxesCallback(LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef)
73 {
74  DIPROPRANGE range = { 0 };
75  range.diph.dwSize = sizeof(range);
76  range.diph.dwHeaderSize = sizeof(range.diph);
77  range.diph.dwHow = DIPH_BYID;
78  range.diph.dwObj = lpddoi->dwType;
79  range.lMax = DInput::AXIS_RANGE;
80  range.lMin = -DInput::AXIS_RANGE;
81  IDirectInputDevice8 *pdev = reinterpret_cast<decltype(pdev)>(pvRef);
82  pdev->SetProperty(DIPROP_RANGE, &range.diph);
83  return DIENUM_CONTINUE;
84 }
85 
86 }
87 
88 void DInput::updateControllers(HWND hwnd, bool foreground, bool exclusive)
89 {
90  debug::writeLine(L"Get controllers...");
91 
92  HRESULT hr = S_OK;
93 
94  m_padInstList.clear();
95  m_pPadDevs.clear();
96 
97  hr = m_pDi->EnumDevices(DI8DEVCLASS_GAMECTRL, enumDevicesCallback,
98  &m_padInstList, DIEDFL_ATTACHEDONLY);
99  checkDXResult<DIError>(hr, "IDirectInput8::EnumDevices() failed");
100 
101  for (const auto &padInst : m_padInstList) {
102  IDirectInputDevice8 *ptmpDevice;
103  hr = m_pDi->CreateDevice(padInst.guidInstance, &ptmpDevice, nullptr);
104  checkDXResult<DIError>(hr, "IDirectInput8::CreateDevice() failed");
105  util::ComPtr<IDirectInputDevice8> pDevice(ptmpDevice);
106 
107  hr = pDevice->SetDataFormat(&c_dfDIJoystick);
108  checkDXResult<DIError>(hr, "IDirectInputDevice8::SetDataFormat() failed");
109  DWORD coopLevel = 0;
110  coopLevel |= (foreground ? DISCL_FOREGROUND : DISCL_BACKGROUND);
111  coopLevel |= (exclusive ? DISCL_EXCLUSIVE : DISCL_NONEXCLUSIVE);
112  hr = pDevice->SetCooperativeLevel(hwnd, coopLevel);
113  checkDXResult<DIError>(hr, "IDirectInputDevice8::SetCooperativeLevel() failed");
114  hr = pDevice->EnumObjects(enumAxesCallback, pDevice.get(), DIDFT_AXIS);
115  checkDXResult<DIError>(hr, "IDirectInputDevice8::EnumObjects() failed");
116 
117  m_pPadDevs.emplace_back(std::move(pDevice));
118  DIJOYSTATE js = { 0 };
119  m_pad.emplace_back(js);
120  }
121 
122  {
123  int i = 0;
124  for (const auto &padInst : m_padInstList) {
125  debug::writef(L"[Controller %d]", i);
126  debug::writef(L"tszInstanceName = %s", padInst.tszInstanceName);
127  debug::writef(L"tszProductName = %s", padInst.tszProductName);
128  i++;
129  }
130  debug::writef(L"%zu controller(s) found", m_pPadDevs.size());
131  }
132 }
133 
135 {
136  HRESULT hr = S_OK;
137 
138  // Keyboard
139  m_key.fill(0);
140  do {
141  hr = m_pKeyDevice->Acquire();
142  } while (hr == DIERR_INPUTLOST);
143  if (hr != DIERR_OTHERAPPHASPRIO) {
144  BYTE tmp[256];
145  hr = m_pKeyDevice->GetDeviceState(sizeof(tmp), &tmp);
146  if (SUCCEEDED(hr)) {
147  for (int i = 0; i < 256; i++) {
148  m_key[i] = ((tmp[i] & 0x80) != 0);
149  }
150  }
151  }
152  else {
153  // DISCL_FOREGROUND and app is background now
154  //debug::writeLine(L"DIERR_OTHERAPPHASPRIO");
155  }
156 
157  // Pad
158  // TODO: think twice about error handling
159  // TODO: sudden disconnect?
160  for (size_t i = 0; i < m_pPadDevs.size(); i++) {
161  ZeroMemory(&m_pad[i], sizeof(m_pad[i]));
162  memset(m_pad[i].rgdwPOV, -1, sizeof(m_pad[i].rgdwPOV));
163 
164  do {
165  hr = m_pPadDevs[i]->Acquire();
166  } while (hr == DIERR_INPUTLOST);
167  if (hr == DIERR_OTHERAPPHASPRIO) {
168  // ISCL_FOREGROUND and app is background now
169  continue;
170  }
171  hr = m_pPadDevs[i]->Poll();
172  hr = m_pPadDevs[i]->GetDeviceState(sizeof(m_pad[i]), &m_pad[i]);
173  // if error, m_pad[i] will be cleared state
174  }
175 }
176 
178 {
179  return m_key;
180 }
181 
183 {
184  ASSERT(m_pad.size() == m_pPadDevs.size());
185  return static_cast<int>(m_pPadDevs.size());
186 }
187 
188 void DInput::getPadState(DIJOYSTATE *state, int index) const
189 {
190  *state = m_pad.at(index);
191 }
192 
193 
194 namespace {
195 
196 struct DikConvEntry {
197  const char *str;
198  const wchar_t *wstr;
199 };
200 
201 #define ENT(s) {s, L##s}
202 const DikConvEntry DikConvTable[DInput::KEY_NUM] = {
203  // 0x00-0xff
204  ENT("?"), ENT("ESCAPE"), ENT("1"), ENT("2"),
205  ENT("3"), ENT("4"), ENT("5"), ENT("6"),
206  ENT("7"), ENT("8"), ENT("9"), ENT("0"),
207  ENT("MINUS"), ENT("EQUALS"), ENT("BACK"), ENT("TAB"),
208  // 0x10-0x1f
209  ENT("Q"), ENT("W"), ENT("E"), ENT("R"),
210  ENT("T"), ENT("Y"), ENT("U"), ENT("I"),
211  ENT("O"), ENT("P"), ENT("LBRACKET"),ENT("RBRACKET"),
212  ENT("RETURN"), ENT("LCONTROL"),ENT("A"), ENT("S"),
213  // 0x20-0x2f
214  ENT("D"), ENT("F"), ENT("G"), ENT("H"),
215  ENT("J"), ENT("K"), ENT("L"), ENT("SEMIC"),
216  ENT("APOST"), ENT("GRAVE"), ENT("LSHIFT"), ENT("BSLASH"),
217  ENT("Z"), ENT("X"), ENT("C"), ENT("V"),
218  // 0x30-0x3f
219  ENT("B"), ENT("N"), ENT("M"), ENT("COMMA"),
220  ENT("PERIOD"), ENT("SLASH"), ENT("RSHIFT"), ENT("MULT"),
221  ENT("LMENU"), ENT("SPACE"), ENT("CAPITAL"), ENT("F1"),
222  ENT("F2"), ENT("F3"), ENT("F4"), ENT("F5"),
223  // 0x40-0x4f
224  ENT("F6"), ENT("F7"), ENT("F8"), ENT("F9"),
225  ENT("F10"), ENT("NUMLOCK"), ENT("SCROLL"), ENT("NUMPAD7"),
226  ENT("NUMPAD8"), ENT("NUMPAD9"), ENT("SUB"), ENT("NUMPAD4"),
227  ENT("NUMPAD5"), ENT("NUMPAD6"), ENT("ADD"), ENT("NUMPAD1"),
228  // 0x50-0x5f
229  ENT("NUMPAD2"), ENT("NUMPAD3"), ENT("NUMPAD0"), ENT("DECIMAL"),
230  ENT("?"), ENT("?"), ENT("OEM_102"), ENT("F11"),
231  ENT("F12"), ENT("?"), ENT("?"), ENT("?"),
232  ENT("?"), ENT("?"), ENT("?"), ENT("?"),
233  // 0x60-0x6f
234  ENT("?"), ENT("?"), ENT("?"), ENT("?"),
235  ENT("F13"), ENT("F14"), ENT("F15"), ENT("?"),
236  ENT("?"), ENT("?"), ENT("?"), ENT("?"),
237  ENT("?"), ENT("?"), ENT("?"), ENT("?"),
238  // 0x70-0x7f
239  ENT("KANA"), ENT("?"), ENT("?"), ENT("ABNT_C1"),
240  ENT("?"), ENT("?"), ENT("?"), ENT("?"),
241  ENT("?"), ENT("CONVERT"), ENT("?"), ENT("NOCONV"),
242  ENT("?"), ENT("YEN"), ENT("ABNT_C2"), ENT("?"),
243  // 0x80-0x8f
244  ENT("?"), ENT("?"), ENT("?"), ENT("?"),
245  ENT("?"), ENT("?"), ENT("?"), ENT("?"),
246  ENT("?"), ENT("?"), ENT("?"), ENT("?"),
247  ENT("?"), ENT("NUMPADEQ"),ENT("?"), ENT("?"),
248  // 0x90-0x9f
249  ENT("PREVTR"), ENT("AT"), ENT("COLON"), ENT("ULINE"),
250  ENT("KANJI"), ENT("STOP"), ENT("AX"), ENT("UNLABEL"),
251  ENT("?"), ENT("NEXTTR"), ENT("?"), ENT("?"),
252  ENT("NUMPADET"),ENT("RCONTROL"),ENT("?"), ENT("?"),
253  // 0xa0-0xaf
254  ENT("MUTE"), ENT("CALC"), ENT("PLYPAUSE"),ENT("?"),
255  ENT("MSTOP"), ENT("?"), ENT("?"), ENT("?"),
256  ENT("?"), ENT("?"), ENT("?"), ENT("?"),
257  ENT("?"), ENT("?"), ENT("VOLDOWN"), ENT("?"),
258  // 0xb0-0xbf
259  ENT("VOLUP"), ENT("?"), ENT("WEBHOME"), ENT("NUMPADCM"),
260  ENT("?"), ENT("DIVIDE"), ENT("?"), ENT("SYSRQ"),
261  ENT("RMENU"), ENT("?"), ENT("?"), ENT("?"),
262  ENT("?"), ENT("?"), ENT("?"), ENT("?"),
263  // 0xc0-0xcf
264  ENT("?"), ENT("?"), ENT("?"), ENT("?"),
265  ENT("?"), ENT("PAUSE"), ENT("?"), ENT("HOME"),
266  ENT("UP"), ENT("PGUP"), ENT("?"), ENT("LEFT"),
267  ENT("?"), ENT("RIGHT"), ENT("?"), ENT("END"),
268  // 0xd0-0xdf
269  ENT("DOWN"), ENT("PGDN"), ENT("INSERT"), ENT("DELETE"),
270  ENT("?"), ENT("?"), ENT("?"), ENT("?"),
271  ENT("?"), ENT("?"), ENT("?"), ENT("LWIN"),
272  ENT("RWIN"), ENT("APPS"), ENT("POWER"), ENT("SLEEP"),
273  // 0xe0-0xef
274  ENT("?"), ENT("?"), ENT("?"), ENT("WAKE"),
275  ENT("?"), ENT("WEBSCH"), ENT("WEBFAV"), ENT("WEBREF"),
276  ENT("WEBSTOP"), ENT("WEBFWD"), ENT("WEBBACK"), ENT("MYCOM"),
277  ENT("MAIL"), ENT("MEDIASEL"),ENT("?"), ENT("?"),
278  // 0xf0-0xff
279  ENT("?"), ENT("?"), ENT("?"), ENT("?"),
280  ENT("?"), ENT("?"), ENT("?"), ENT("?"),
281  ENT("?"), ENT("?"), ENT("?"), ENT("?"),
282  ENT("?"), ENT("?"), ENT("?"), ENT("?"),
283 };
284 #undef ENT
285 
286 } // namespace
287 
288 const char *dikToString(int dik)
289 {
290  if (dik < 0 || dik >= 256) {
291  throw std::logic_error("invalid DIK");
292  }
293  return DikConvTable[dik].str;
294 }
295 
296 const wchar_t *dikToWString(int dik)
297 {
298  if (dik < 0 || dik >= 256) {
299  throw std::logic_error("invalid DIK");
300  }
301  return DikConvTable[dik].wstr;
302 }
303 
304 } // namespace input
305 } // namespace yappy
int getPadCount() const
Get connected pad count.
Definition: input.cpp:182
void updateControllers(HWND hwnd, bool foreground=true, bool exclusive=false)
Update pad list.
Definition: input.cpp:88
const char * str
Definition: input.cpp:197
DInput(HINSTANCE hInst, HWND hWnd, bool foreground=true, bool exclusive=false)
Initialize DirectInput8.
Definition: input.cpp:15
Debug utilities.
const wchar_t * wstr
Definition: input.cpp:198
void writef(const wchar_t *fmt,...) noexcept
Write debug message using format string like printf.
Definition: debug.cpp:103
~DInput()
Finalize DirectInput8.
Definition: input.cpp:52
const char * dikToString(int dik)
Convert DIK_XXX to string.
Definition: input.cpp:288
KeyData getKeys() const
Get keyboard input data.
Definition: input.cpp:177
#define ASSERT(x)
Assertion which uses debug framework.
Definition: debug.h:18
Definition: config.cpp:6
void checkDXResult(HRESULT hr, const std::string &msg)
Definition: exceptions.h:83
void writeLine(const wchar_t *str=L"") noexcept
Write debug string and new line.
Definition: debug.h:64
static const int KEY_NUM
Keyboard array count.
Definition: input.h:19
void processFrame()
Must be called at every frame.
Definition: input.cpp:134
const wchar_t * dikToWString(int dik)
Convert DIK_XXX to wide-string.
Definition: input.cpp:296
#define DIRECTINPUT_VERSION
Definition: input.h:4
std::array< bool, KEY_NUM > KeyData
Keyboard data: array[KEY_NUM] of bool.
Definition: input.h:28
void getPadState(DIJOYSTATE *state, int index) const
Get pad input data.
Definition: input.cpp:188
#define ENT(s)
Definition: input.cpp:201
std::unique_ptr< T, ComDeleter > ComPtr
unique_ptr of IUnknown with ComDeleter.
Definition: util.h:88
static const int AXIS_RANGE
Pad analog value max.
Definition: input.h:21