00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "CMSWindowsXInput.h"
00020 #include "XScreen.h"
00021 #include "CThread.h"
00022 #include "TMethodJob.h"
00023 #include "CLog.h"
00024 #include "XInputHook.h"
00025 #include "CMSWindowsScreen.h"
00026
00027 #include "XInput.h"
00028
00029 typedef DWORD (WINAPI *XInputGetStateFunc)(DWORD, XINPUT_STATE*);
00030 typedef DWORD (WINAPI *XInputSetStateFunc)(DWORD, XINPUT_VIBRATION*);
00031
00032 CMSWindowsXInput::CMSWindowsXInput(CMSWindowsScreen* screen, const CGameDeviceInfo& gameDeviceInfo) :
00033 m_screen(screen),
00034 m_gameDeviceInfo(gameDeviceInfo),
00035 m_xInputPollThread(NULL),
00036 m_xInputTimingThread(NULL),
00037 m_xInputFeedbackThread(NULL),
00038 m_gameButtonsLast(0),
00039 m_gameLeftTriggerLast(0),
00040 m_gameRightTriggerLast(0),
00041 m_gameLeftStickXLast(0),
00042 m_gameLeftStickYLast(0),
00043 m_gameRightStickXLast(0),
00044 m_gameRightStickYLast(0),
00045 m_gameLastTimingSent(0),
00046 m_gameTimingWaiting(false),
00047 m_gameFakeLag(0),
00048 m_gamePollFreq(kGamePollFreqDefault),
00049 m_gamePollFreqAdjust(0),
00050 m_gameFakeLagMin(kGamePollFreqDefault),
00051 m_gameTimingStarted(false),
00052 m_gameTimingFirst(0),
00053 m_gameFakeLagLast(0),
00054 m_gameTimingCalibrated(false),
00055 m_xinputModule(NULL)
00056 {
00057 m_xinputModule = LoadLibrary("xinput1_3.dll");
00058 if (m_xinputModule == NULL)
00059 {
00060 throw XScreenXInputFailure("could not load xinput library");
00061 }
00062
00063 if (screen->isPrimary())
00064 {
00065
00066 m_xInputPollThread = new CThread(new TMethodJob<CMSWindowsXInput>(
00067 this, &CMSWindowsXInput::xInputPollThread));
00068 }
00069 else
00070 {
00071
00072 m_xInputTimingThread = new CThread(new TMethodJob<CMSWindowsXInput>(
00073 this, &CMSWindowsXInput::xInputTimingThread));
00074
00075
00076 m_xInputFeedbackThread = new CThread(new TMethodJob<CMSWindowsXInput>(
00077 this, &CMSWindowsXInput::xInputFeedbackThread));
00078 }
00079 }
00080
00081 CMSWindowsXInput::~CMSWindowsXInput()
00082 {
00083 }
00084
00085 void
00086 CMSWindowsXInput::fakeGameDeviceButtons(GameDeviceID id, GameDeviceButton buttons) const
00087 {
00088 SetXInputButtons(id, buttons);
00089 }
00090
00091 void
00092 CMSWindowsXInput::fakeGameDeviceSticks(GameDeviceID id, SInt16 x1, SInt16 y1, SInt16 x2, SInt16 y2) const
00093 {
00094 SetXInputSticks(id, x1, y1, x2, y2);
00095 }
00096
00097 void
00098 CMSWindowsXInput::fakeGameDeviceTriggers(GameDeviceID id, UInt8 t1, UInt8 t2) const
00099 {
00100 SetXInputTriggers(id, t1, t2);
00101 }
00102
00103 void
00104 CMSWindowsXInput::queueGameDeviceTimingReq() const
00105 {
00106 QueueXInputTimingReq();
00107 }
00108
00109 void
00110 CMSWindowsXInput::gameDeviceTimingResp(UInt16 freq)
00111 {
00112 if (!m_gameTimingStarted)
00113 {
00114
00115 m_gameTimingFirst = (UInt16)(ARCH->time() * 1000);
00116 m_gameTimingStarted = true;
00117 }
00118
00119 m_gameTimingWaiting = false;
00120 m_gameFakeLagLast = m_gameFakeLag;
00121 m_gameFakeLag = (UInt16)((ARCH->time() - m_gameLastTimingSent) * 1000);
00122 m_gameFakeLagRecord.push_back(m_gameFakeLag);
00123
00124 if (m_gameFakeLag < m_gameFakeLagMin)
00125 {
00126
00127
00128 m_gameFakeLagMin = m_gameFakeLag;
00129 }
00130 else if (m_gameFakeLag > (m_gameFakeLagLast * 2))
00131 {
00132
00133
00134 m_gameFakeLagMin = m_gameFakeLagLast;
00135 }
00136
00137
00138 if (freq > kGamePollFreqMin && freq < kGamePollFreqMax)
00139 {
00140 m_gamePollFreq = freq;
00141 }
00142
00143 UInt16 timeSinceStart = ((UInt16)(ARCH->time() * 1000) - m_gameTimingFirst);
00144 if (!m_gameTimingCalibrated && (timeSinceStart < kGameCalibrationPeriod))
00145 {
00146
00147
00148 m_gamePollFreqAdjust = 1;
00149 LOG((CLOG_DEBUG2 "calibrating game device poll frequency, start=%d, now=%d, since=%d",
00150 m_gameTimingFirst, (int)(ARCH->time() * 1000), timeSinceStart));
00151 }
00152 else
00153 {
00154
00155
00156
00157
00158 m_gameTimingCalibrated = true;
00159
00160
00161 m_gamePollFreqAdjust = 0;
00162 if (m_gameFakeLag > m_gameFakeLagMin * 3)
00163 {
00164 m_gamePollFreqAdjust = 1;
00165 }
00166 else if (m_gameFakeLag < m_gameFakeLagMin)
00167 {
00168 m_gamePollFreqAdjust = -1;
00169 }
00170 }
00171
00172 LOG((CLOG_DEBUG3 "game device timing, lag=%dms, freq=%dms, adjust=%dms, min=%dms",
00173 m_gameFakeLag, m_gamePollFreq, m_gamePollFreqAdjust, m_gameFakeLagMin));
00174
00175 if (m_gameFakeLagRecord.size() >= kGameLagRecordMax)
00176 {
00177 UInt16 v, min = 65535, max = 0, total = 0;
00178 std::vector<UInt16>::iterator it;
00179 for (it = m_gameFakeLagRecord.begin(); it < m_gameFakeLagRecord.end(); ++it)
00180 {
00181 v = *it;
00182 if (v < min)
00183 {
00184 min = v;
00185 }
00186 if (v > max)
00187 {
00188 max = v;
00189 }
00190 total += v;
00191 }
00192
00193 LOG((CLOG_INFO "game device timing, min=%dms, max=%dms, avg=%dms",
00194 min, max, (UInt16)(total / m_gameFakeLagRecord.size())));
00195 m_gameFakeLagRecord.clear();
00196 }
00197 }
00198
00199 void
00200 CMSWindowsXInput::gameDeviceFeedback(GameDeviceID id, UInt16 m1, UInt16 m2)
00201 {
00202 XInputSetStateFunc xInputSetStateFunc =
00203 (XInputSetStateFunc)GetProcAddress(m_xinputModule, "XInputSetState");
00204
00205 if (xInputSetStateFunc == NULL)
00206 {
00207 throw XScreenXInputFailure("could not get function address: XInputSetState");
00208 }
00209
00210 XINPUT_VIBRATION vibration;
00211 ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION));
00212 vibration.wLeftMotorSpeed = m1;
00213 vibration.wRightMotorSpeed = m2;
00214 xInputSetStateFunc(id, &vibration);
00215 }
00216
00217 void
00218 CMSWindowsXInput::xInputPollThread(void*)
00219 {
00220 LOG((CLOG_DEBUG "xinput poll thread started"));
00221
00222 XInputGetStateFunc xInputGetStateFunc =
00223 (XInputGetStateFunc)GetProcAddress(m_xinputModule, "XInputGetState");
00224
00225 if (xInputGetStateFunc == NULL)
00226 {
00227 throw XScreenXInputFailure("could not get function address: XInputGetState");
00228 }
00229
00230 int index = 0;
00231 XINPUT_STATE state;
00232 bool end = false;
00233 while (!end)
00234 {
00235 ZeroMemory(&state, sizeof(XINPUT_STATE));
00236 DWORD result = xInputGetStateFunc(index, &state);
00237
00238
00239 if (m_gameTimingWaiting && (ARCH->time() - m_gameLastTimingSent > 2))
00240 {
00241 m_gameTimingWaiting = false;
00242 LOG((CLOG_DEBUG "game device timing request timed out"));
00243 }
00244
00245
00246 if (result == ERROR_SUCCESS)
00247 {
00248
00249 bool buttonsChanged = state.Gamepad.wButtons != m_gameButtonsLast;
00250 bool leftTriggerChanged = state.Gamepad.bLeftTrigger != m_gameLeftTriggerLast;
00251 bool rightTriggerChanged = state.Gamepad.bRightTrigger != m_gameRightTriggerLast;
00252 bool leftStickXChanged = state.Gamepad.sThumbLX != m_gameLeftStickXLast;
00253 bool leftStickYChanged = state.Gamepad.sThumbLY != m_gameLeftStickYLast;
00254 bool rightStickXChanged = state.Gamepad.sThumbRX != m_gameRightStickXLast;
00255 bool rightStickYChanged = state.Gamepad.sThumbRY != m_gameRightStickYLast;
00256
00257 m_gameButtonsLast = state.Gamepad.wButtons;
00258 m_gameLeftTriggerLast = state.Gamepad.bLeftTrigger;
00259 m_gameRightTriggerLast = state.Gamepad.bRightTrigger;
00260 m_gameLeftStickXLast = state.Gamepad.sThumbLX;
00261 m_gameLeftStickYLast = state.Gamepad.sThumbLY;
00262 m_gameRightStickXLast = state.Gamepad.sThumbRX;
00263 m_gameRightStickYLast = state.Gamepad.sThumbRY;
00264
00265 bool eventSent = false;
00266
00267 if (buttonsChanged)
00268 {
00269 LOG((CLOG_DEBUG "xinput buttons changed"));
00270
00271
00272 m_screen->sendEvent(m_screen->getGameDeviceButtonsEvent(),
00273 new IPrimaryScreen::CGameDeviceButtonInfo(index, state.Gamepad.wButtons));
00274
00275 eventSent = true;
00276 }
00277
00278 if (leftStickXChanged || leftStickYChanged || rightStickXChanged || rightStickYChanged)
00279 {
00280 LOG((CLOG_DEBUG "xinput sticks changed"));
00281
00282 m_screen->sendEvent(m_screen->getGameDeviceSticksEvent(),
00283 new IPrimaryScreen::CGameDeviceStickInfo(
00284 index,
00285 m_gameLeftStickXLast, m_gameLeftStickYLast,
00286 m_gameRightStickXLast, m_gameRightStickYLast));
00287
00288 eventSent = true;
00289 }
00290
00291 if (leftTriggerChanged || rightTriggerChanged)
00292 {
00293 LOG((CLOG_DEBUG "xinput triggers changed"));
00294
00295
00296 m_screen->sendEvent(m_screen->getGameDeviceTriggersEvent(),
00297 new IPrimaryScreen::CGameDeviceTriggerInfo(
00298 index,
00299 state.Gamepad.bLeftTrigger,
00300 state.Gamepad.bRightTrigger));
00301
00302 eventSent = true;
00303 }
00304
00305 if (!m_gameTimingWaiting && (ARCH->time() - m_gameLastTimingSent > .5))
00306 {
00307 m_screen->sendEvent(m_screen->getGameDeviceTimingReqEvent(), NULL);
00308 m_gameLastTimingSent = ARCH->time();
00309 m_gameTimingWaiting = true;
00310
00311 LOG((CLOG_DEBUG "game device timing request at %.4f", m_gameLastTimingSent));
00312 }
00313 }
00314
00315 UInt16 sleep = m_gamePollFreq + m_gamePollFreqAdjust;
00316 LOG((CLOG_DEBUG5 "xinput poll sleeping for %dms", sleep));
00317 Sleep(sleep);
00318 }
00319 }
00320
00321 void
00322 CMSWindowsXInput::xInputTimingThread(void*)
00323 {
00324 LOG((CLOG_DEBUG "xinput timing thread started"));
00325
00326 bool end = false;
00327 while (!end)
00328 {
00329
00330
00331
00332 if (DequeueXInputTimingResp())
00333 {
00334 LOG((CLOG_DEBUG "dequeued game device timing response"));
00335 m_screen->sendEvent(m_screen->getGameDeviceTimingRespEvent(),
00336 new IPrimaryScreen::CGameDeviceTimingRespInfo(GetXInputFakeFreqMillis()));
00337 }
00338
00339
00340 Sleep(1);
00341 }
00342 }
00343
00344 void
00345 CMSWindowsXInput::xInputFeedbackThread(void*)
00346 {
00347 LOG((CLOG_DEBUG "xinput feedback thread started"));
00348
00349 int index = 0;
00350 bool end = false;
00351 while (!end)
00352 {
00353 WORD leftMotor, rightMotor;
00354 if (DequeueXInputFeedback(&leftMotor, &rightMotor))
00355 {
00356 LOG((CLOG_DEBUG "dequeued game device feedback"));
00357 m_screen->sendEvent(m_screen->getGameDeviceFeedbackEvent(),
00358 new IPrimaryScreen::CGameDeviceFeedbackInfo(index, leftMotor, rightMotor));
00359 }
00360
00361 Sleep(50);
00362 }
00363 }