Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1937 lines
55 KiB

  1. /*****************************************************************************
  2. *
  3. * DIEm.c
  4. *
  5. * Copyright (c) 1996 Microsoft Corporation. All Rights Reserved.
  6. *
  7. * Abstract:
  8. *
  9. * DirectInput VxD emulation layer. (I.e., do the things that
  10. * dinput.vxd normally does.) You may find large chunks of this
  11. * code familiar: It's exactly the same thing that happens in
  12. * the VxD.
  13. *
  14. * Contents:
  15. *
  16. * CEm_AcquireInstance
  17. * CEm_UnacquireInstance
  18. * CEm_SetBufferSize
  19. * CEm_DestroyInstance
  20. * CEm_SetDataFormat
  21. *
  22. *****************************************************************************/
  23. #include "dinputpr.h"
  24. /*****************************************************************************
  25. *
  26. * The sqiffle for this file.
  27. *
  28. *****************************************************************************/
  29. #define sqfl sqflEm
  30. #define ThisClass CEm
  31. #define CEM_SIGNATURE 0x4D4D4545 /* "EEMM" */
  32. PEM g_pemFirst;
  33. #ifdef WORKER_THREAD
  34. PLLTHREADSTATE g_plts; /* The currently active input thread */
  35. #ifdef USE_WM_INPUT
  36. BOOL g_fFromKbdMse;
  37. #endif
  38. #endif /* WORKER_THREAD */
  39. /*****************************************************************************
  40. *
  41. * @doc INTERNAL
  42. *
  43. * @func void | CEm_FreeInstance |
  44. *
  45. * It's really gone now.
  46. *
  47. * @parm PEM | this |
  48. *
  49. * The victim.
  50. *
  51. *****************************************************************************/
  52. void EXTERNAL
  53. CEm_FreeInstance(PEM this)
  54. {
  55. PEM *ppem;
  56. EnterProc(CEm_FreeInstance, (_ "p", this));
  57. AssertF(this->dwSignature == CEM_SIGNATURE);
  58. AssertF(this->cRef == 0);
  59. /*
  60. * It is the owner's responsibility to unacquire before releasing.
  61. */
  62. AssertF(!(this->vi.fl & VIFL_ACQUIRED));
  63. /*
  64. * If this device has a reference to a hook, then remove
  65. * the reference.
  66. */
  67. #ifdef WORKER_THREAD
  68. if (this->fWorkerThread) {
  69. PLLTHREADSTATE plts;
  70. DWORD idThread;
  71. /*
  72. * Protect test and access of g_plts with DLLCrit
  73. */
  74. DllEnterCrit();
  75. plts = g_plts;
  76. if (plts ) {
  77. AssertF(plts->cRef);
  78. /*
  79. * Note that we need to keep the thread ID because
  80. * the InterlockedDecrement might cause us to lose
  81. * the object.
  82. *
  83. * Note that this opens a race condition where the
  84. * thread might decide to kill itself before we
  85. * post it the nudge message. That's okay, because
  86. * even if the thread ID gets recycled, the message
  87. * that appears is a dummy WM_NULL message that
  88. * causes no harm.
  89. */
  90. idThread = plts->idThread; /* Must save before we dec */
  91. if( InterlockedDecrement(&plts->cRef) == 0 ) {
  92. g_plts = 0;
  93. }
  94. }
  95. DllLeaveCrit();
  96. if( plts )
  97. {
  98. NudgeWorkerThread(idThread);
  99. }
  100. }
  101. #endif
  102. /*
  103. * Unlink the node from the master list.
  104. */
  105. DllEnterCrit();
  106. for (ppem = &g_pemFirst; *ppem; ppem = &(*ppem)->pemNext) {
  107. AssertF((*ppem)->dwSignature == CEM_SIGNATURE);
  108. if (*ppem == this) {
  109. *ppem = (*ppem)->pemNext;
  110. break;
  111. }
  112. }
  113. AssertF(ppem);
  114. DllLeaveCrit();
  115. FreePpv(&this->rgdwDf);
  116. FreePpv(&this->vi.pBuffer);
  117. if( InterlockedDecrement(&this->ped->cRef) == 0x0 )
  118. {
  119. FreePpv(&this->ped->pDevType);
  120. }
  121. D(this->dwSignature++);
  122. FreePv(this);
  123. ExitProc();
  124. }
  125. /*****************************************************************************
  126. *
  127. * @doc INTERNAL
  128. *
  129. * @func HRESULT | CEm_CreateInstance |
  130. *
  131. * Create a device thing.
  132. *
  133. * @parm PVXDDEVICEFORMAT | pdevf |
  134. *
  135. * What the object should look like.
  136. *
  137. * @parm PVXDINSTANCE * | ppviOut |
  138. *
  139. * The answer goes here.
  140. *
  141. * @parm PED | ped |
  142. *
  143. * Descriptor.
  144. *
  145. *****************************************************************************/
  146. HRESULT EXTERNAL
  147. CEm_CreateInstance(PVXDDEVICEFORMAT pdevf, PVXDINSTANCE *ppviOut, PED ped)
  148. {
  149. HRESULT hres;
  150. EnterProc(CEm_CreateInstance, (_ "pp", pdevf, ped));
  151. AssertF(pdevf->cbData == ped->cbData);
  152. CAssertF(FIELD_OFFSET(CEm, vi) == 0);
  153. hres = AllocCbPpv(cbX(CEm), ppviOut);
  154. if (SUCCEEDED(hres)) {
  155. PEM pem = (PV)*ppviOut;
  156. D(pem->dwSignature = CEM_SIGNATURE);
  157. pem->dwExtra = pdevf->dwExtra;
  158. pem->ped = ped;
  159. pem->cAcquire = -1;
  160. /*
  161. * Make sure these functions are inverses.
  162. */
  163. AssertF(DIGETEMFL(DIMAKEEMFL(pdevf->dwEmulation)) ==
  164. pdevf->dwEmulation);
  165. pem->vi.fl = VIFL_EMULATED | DIMAKEEMFL(pdevf->dwEmulation);
  166. pem->vi.pState = ped->pState;
  167. CEm_AddRef(pem);
  168. DllEnterCrit();
  169. /*
  170. * Build the devtype array. This consists of one dword
  171. * for each byte in the data format.
  172. *
  173. * Someday: Do the button thing too.
  174. */
  175. if (ped->pDevType == 0) {
  176. hres = ReallocCbPpv(cbCdw(pdevf->cbData), &ped->pDevType);
  177. if (SUCCEEDED(hres)) {
  178. UINT iobj;
  179. /*
  180. * If HID is messed up, we will end up with
  181. * entries whose dwType is zero (because HID
  182. * said they existed, but when we went around
  183. * enumerating, they never showed up).
  184. *
  185. * And don't put no-data items into the array!
  186. */
  187. for (iobj = 0; iobj < pdevf->cObj; iobj++) {
  188. if (pdevf->rgodf[iobj].dwType &&
  189. !(pdevf->rgodf[iobj].dwType & DIDFT_NODATA)) {
  190. ped->pDevType[pdevf->rgodf[iobj].dwOfs] =
  191. pdevf->rgodf[iobj].dwType;
  192. }
  193. }
  194. }
  195. } else {
  196. hres = S_OK;
  197. }
  198. if (SUCCEEDED(hres)) {
  199. /*
  200. * Link this node into the list. This must be done
  201. * under the critical section.
  202. */
  203. pem->pemNext = g_pemFirst;
  204. g_pemFirst = pem;
  205. InterlockedIncrement(&ped->cRef);
  206. *ppviOut = &pem->vi;
  207. } else {
  208. FreePpv(ppviOut);
  209. }
  210. DllLeaveCrit();
  211. }
  212. ExitOleProcPpv(ppviOut);
  213. return hres;
  214. }
  215. /*****************************************************************************
  216. *
  217. * @doc INTERNAL
  218. *
  219. * @func DWORD | CEm_NextSequence |
  220. *
  221. * Increment the sequence number wherever it may be.
  222. *
  223. *****************************************************************************/
  224. DWORD INTERNAL
  225. CEm_NextSequence(void)
  226. {
  227. /*
  228. * Stashing the value into a local tells the compiler that
  229. * the value can be cached. Otherwise, the compiler has
  230. * to assume that InterlockedIncrement can modify g_pdwSequence
  231. * so it keeps reloading it.
  232. */
  233. LPDWORD pdwSequence = g_pdwSequence;
  234. AssertF(pdwSequence);
  235. /*
  236. * Increment through zero.
  237. */
  238. if (InterlockedIncrement((LPLONG)pdwSequence) == 0) {
  239. InterlockedIncrement((LPLONG)pdwSequence);
  240. }
  241. return *pdwSequence;
  242. }
  243. /*****************************************************************************
  244. *
  245. * @doc INTERNAL
  246. *
  247. * @func PEM | CEm_BufferEvent |
  248. *
  249. * Add a single event to the device, returning the next device
  250. * on the global list.
  251. *
  252. * This routine is entered with the global critical section
  253. * taken exactly once.
  254. *
  255. *****************************************************************************/
  256. PEM INTERNAL
  257. CEm_BufferEvent(PEM pem, DWORD dwData, DWORD dwOfs, DWORD tm, DWORD dwSeq)
  258. {
  259. PEM pemNext;
  260. /*
  261. * We must release the global critical section in order to take
  262. * the device critical section.
  263. */
  264. CEm_AddRef(pem); /* Make sure it doesn't vanish */
  265. DllLeaveCrit();
  266. AssertF(!InCrit());
  267. /*
  268. * ---Windows Bug 238305---
  269. * Run the buffering code in __try block so that if an
  270. * input is receive after the device is released, we can
  271. * catch the AV and clean up from there.
  272. */
  273. __try
  274. {
  275. CDIDev_EnterCrit(pem->vi.pdd);
  276. AssertF(dwOfs < pem->ped->cbData);
  277. AssertF(pem->rgdwDf);
  278. /*
  279. * If the user cares about the object...
  280. */
  281. if (pem->rgdwDf[dwOfs] != 0xFFFFFFFF) {
  282. LPDIDEVICEOBJECTDATA_DX3 pdod = pem->vi.pHead;
  283. /*
  284. * Set the node value.
  285. */
  286. pdod->dwOfs = pem->rgdwDf[dwOfs];
  287. pdod->dwData = dwData;
  288. pdod->dwTimeStamp = tm;
  289. pdod->dwSequence = dwSeq;
  290. /*
  291. * Append the node to the list if there is room.
  292. * Note that we rely above on the fact that the list is
  293. * never totally full.
  294. */
  295. pdod++;
  296. AssertF(pdod <= pem->vi.pEnd);
  297. if (pdod >= pem->vi.pEnd) {
  298. pdod = pem->vi.pBuffer;
  299. }
  300. /*
  301. * always keep the new data
  302. */
  303. pem->vi.pHead = pdod;
  304. if (pdod == pem->vi.pTail) {
  305. if (!pem->vi.fOverflow) {
  306. RPF("Buffer overflow; discard old data");
  307. }
  308. pem->vi.pTail++;
  309. if (pem->vi.pTail == pem->vi.pEnd) {
  310. pem->vi.pTail = pem->vi.pBuffer;
  311. }
  312. pem->vi.fOverflow = 1;
  313. }
  314. }
  315. CDIDev_LeaveCrit(pem->vi.pdd);
  316. }
  317. /*
  318. * If we get an AV, most likely input is received after the device has
  319. * been released. In this case, we clean up the thread and exit as
  320. * soon as possible.
  321. */
  322. __except( GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
  323. EXCEPTION_EXECUTE_HANDLER :
  324. EXCEPTION_CONTINUE_SEARCH )
  325. {
  326. /* Do nothing here, so we clean up the thread and exit below. */
  327. RPF("CEm_BufferEvent: Access Violation catched! Most likely the device has been released");
  328. }
  329. DllEnterCrit();
  330. pemNext = pem->pemNext;
  331. AssertF(fLimpFF(pemNext, pemNext->dwSignature == CEM_SIGNATURE));
  332. CEm_Release(pem);
  333. return pemNext;
  334. }
  335. /*****************************************************************************
  336. *
  337. * @doc EXTERNAL
  338. *
  339. * @func HRESULT | CEm_ContinueEvent |
  340. *
  341. * Add a single event to the queues of all acquired devices
  342. * of the indicated type.
  343. *
  344. * @returns
  345. *
  346. * TRUE if someone is interested in this data (even if they are not
  347. * buffered).
  348. *
  349. *****************************************************************************/
  350. BOOL EXTERNAL
  351. CEm_ContinueEvent(PED ped, DWORD dwData, DWORD dwOfs, DWORD tm, DWORD dwSeq)
  352. {
  353. DWORD ddwData; /* delta in dwData */
  354. BOOL fRtn = FALSE;
  355. AssertF(!InCrit());
  356. /* Sanity check: Make sure the ped has been initialized */
  357. if (ped->pDevType) {
  358. PEM pem, pemNext;
  359. if (ped->pDevType[dwOfs] & DIDFT_DWORDOBJS) {
  360. DWORD UNALIGNED *pdw = pvAddPvCb(ped->pState, dwOfs);
  361. if (*pdw != dwData) {
  362. if (ped->pDevType[dwOfs] & DIDFT_POV ) {
  363. ddwData = dwData; /* Don't do deltas for POV */
  364. } else {
  365. ddwData = dwData - *pdw;
  366. }
  367. *pdw = dwData;
  368. } else {
  369. goto nop;
  370. }
  371. } else {
  372. LPBYTE pb = pvAddPvCb(ped->pState, dwOfs);
  373. AssertF((dwData & ~0x80) == 0);
  374. if (*pb != (BYTE)dwData) {
  375. *pb = (BYTE)dwData;
  376. ddwData = dwData; /* Don't do deltas for buttons */
  377. /* Someday: Button sequences go here */
  378. } else {
  379. goto nop;
  380. }
  381. }
  382. AssertF(!InCrit()); /* You can never be too paranoid */
  383. DllEnterCrit();
  384. for (pem = g_pemFirst; pem; pem = pemNext) {
  385. AssertF(pem->dwSignature == CEM_SIGNATURE);
  386. if ((pem->vi.fl & (VIFL_ACQUIRED|VIFL_INITIALIZE)) && pem->ped == ped) {
  387. if (pem->vi.pBuffer) {
  388. if( pem->vi.fl & VIFL_RELATIVE )
  389. {
  390. pemNext = CEm_BufferEvent(pem, ddwData, dwOfs, tm, dwSeq);
  391. }
  392. else
  393. {
  394. pemNext = CEm_BufferEvent(pem, dwData, dwOfs, tm, dwSeq);
  395. }
  396. AssertF(fLimpFF(pemNext,
  397. pemNext->dwSignature == CEM_SIGNATURE));
  398. } else {
  399. pemNext = pem->pemNext;
  400. AssertF(fLimpFF(pemNext,
  401. pemNext->dwSignature == CEM_SIGNATURE));
  402. }
  403. /*
  404. * It would be easy to avoid setting the event if nothing
  405. * was buffered but somebody would rely on getting them
  406. * without setting a buffer.
  407. */
  408. fRtn = TRUE;
  409. } else {
  410. pemNext = pem->pemNext;
  411. AssertF(fLimpFF(pemNext,
  412. pemNext->dwSignature == CEM_SIGNATURE));
  413. }
  414. }
  415. DllLeaveCrit();
  416. }
  417. nop:;
  418. return fRtn;
  419. }
  420. /*****************************************************************************
  421. *
  422. * @doc INTERNAL
  423. *
  424. * @func DWORD | CEm_AddEvent |
  425. *
  426. * Increment the DirectInput sequence number, then
  427. * add a single event to the queues of all acquired devices
  428. * of the indicated type.
  429. *
  430. * @parm PED | ped |
  431. *
  432. * Device which is adding the event.
  433. *
  434. * @parm DWORD | dwData |
  435. *
  436. * The event data.
  437. *
  438. * @parm DWORD | dwOfs |
  439. *
  440. * Device data format-relative offset for <p dwData>.
  441. *
  442. * @parm DWORD | tm |
  443. *
  444. * Time the event was generated.
  445. *
  446. * @returns
  447. *
  448. * Returns the sequence number added, so that it may be
  449. * continued.
  450. *
  451. *****************************************************************************/
  452. DWORD EXTERNAL
  453. CEm_AddEvent(PED ped, DWORD dwData, DWORD dwOfs, DWORD tm)
  454. {
  455. PEM pem, pemNext;
  456. DWORD dwSeq = CEm_NextSequence();
  457. AssertF(!InCrit()); /* You can never be too paranoid */
  458. if( CEm_ContinueEvent(ped, dwData, dwOfs, tm, dwSeq) )
  459. {
  460. DllEnterCrit();
  461. for (pem = g_pemFirst; pem; pem = pemNext) {
  462. AssertF(pem->dwSignature == CEM_SIGNATURE);
  463. if ((pem->vi.fl & VIFL_ACQUIRED) && pem->ped == ped) {
  464. CDIDev_SetNotifyEvent(pem->vi.pdd);
  465. }
  466. pemNext = pem->pemNext;
  467. AssertF(fLimpFF(pemNext,
  468. pemNext->dwSignature == CEM_SIGNATURE));
  469. }
  470. DllLeaveCrit();
  471. }
  472. return dwSeq;
  473. }
  474. /*****************************************************************************
  475. *
  476. * @doc INTERNAL
  477. *
  478. * @func HRESULT | CEm_AddState |
  479. *
  480. * Record a brand new device state.
  481. *
  482. * @parm PED | ped |
  483. *
  484. * Device which has changed state.
  485. *
  486. * @parm DWORD | dwData |
  487. *
  488. * The value to record.
  489. *
  490. * @parm DWORD | tm |
  491. *
  492. * Time the state change was generated.
  493. *
  494. *****************************************************************************/
  495. void EXTERNAL
  496. CEm_AddState(PED ped, LPVOID pvData, DWORD tm)
  497. {
  498. DWORD dwSeq = CEm_NextSequence();
  499. /* Sanity check: Make sure the ped has been initialized */
  500. if (ped->pDevType) {
  501. DWORD dwOfs;
  502. BOOL fEvent = FALSE;
  503. /*
  504. * Note, it is too late to improve performance by only doing events
  505. * if somebody is listening.
  506. */
  507. dwOfs = 0;
  508. while (dwOfs < ped->cbData) {
  509. /*
  510. * There shouldn't be any no-data items.
  511. */
  512. AssertF(!(ped->pDevType[dwOfs] & DIDFT_NODATA));
  513. if (ped->pDevType[dwOfs] & DIDFT_DWORDOBJS) {
  514. DWORD UNALIGNED *pdw = pvAddPvCb(pvData, dwOfs);
  515. if( CEm_ContinueEvent(ped, *pdw, dwOfs, tm, dwSeq) ){
  516. fEvent = TRUE;
  517. }
  518. dwOfs += cbX(DWORD);
  519. } else {
  520. LPBYTE pb = pvAddPvCb(pvData, dwOfs);
  521. if( CEm_ContinueEvent(ped, *pb, dwOfs, tm, dwSeq) ) {
  522. fEvent = TRUE;
  523. }
  524. dwOfs++;
  525. }
  526. }
  527. if( fEvent ) {
  528. PEM pem, pemNext;
  529. AssertF(!InCrit()); /* You can never be too paranoid */
  530. DllEnterCrit();
  531. for (pem = g_pemFirst; pem; pem = pemNext) {
  532. AssertF(pem->dwSignature == CEM_SIGNATURE);
  533. if ((pem->vi.fl & VIFL_ACQUIRED) && pem->ped == ped) {
  534. CDIDev_SetNotifyEvent(pem->vi.pdd);
  535. }
  536. pemNext = pem->pemNext;
  537. AssertF(fLimpFF(pemNext,
  538. pemNext->dwSignature == CEM_SIGNATURE));
  539. }
  540. DllLeaveCrit();
  541. }
  542. }
  543. }
  544. #if 0
  545. /*****************************************************************************
  546. *
  547. * @doc INTERNAL
  548. *
  549. * @func HRESULT | CEm_InputLost |
  550. *
  551. * Remove global hooks because something weird happened.
  552. *
  553. * We don't need to do anything because our hooks are local.
  554. *
  555. *****************************************************************************/
  556. HRESULT INLINE
  557. CEm_InputLost(LPVOID pvIn, LPVOID pvOut)
  558. {
  559. return S_OK;
  560. }
  561. #endif
  562. /*****************************************************************************
  563. *
  564. * @doc INTERNAL
  565. *
  566. * @func HRESULT | CEm_UnacquirePem |
  567. *
  568. * Unacquire the device in the device-specific way.
  569. *
  570. * @parm PEM | pem |
  571. *
  572. * Information about the gizmo being mangled.
  573. *
  574. * @parm UINT | fdufl |
  575. *
  576. * Assorted flags describing why we are being unacquired.
  577. *
  578. *****************************************************************************/
  579. HRESULT INTERNAL
  580. CEm_UnacquirePem(PEM this, UINT fdufl)
  581. {
  582. HRESULT hres;
  583. #ifdef DEBUG
  584. EnterProcR(CEm_UnacquirePem, (_ "px", this, fdufl));
  585. #else
  586. EnterProcR(IDirectInputDevice8::Unacquire, (_ "p", this));
  587. #endif
  588. AssertF(this->dwSignature == CEM_SIGNATURE);
  589. AssertF((fdufl & ~FDUFL_UNPLUGGED) == 0);
  590. CAssertF(FDUFL_UNPLUGGED == VIFL_UNPLUGGED);
  591. if (this->vi.fl & VIFL_ACQUIRED) {
  592. this->vi.fl &= ~VIFL_ACQUIRED;
  593. this->vi.fl |= fdufl;
  594. if (InterlockedDecrement(&this->cAcquire) < 0) {
  595. InterlockedDecrement(&this->ped->cAcquire);
  596. hres = this->ped->Acquire(this, 0);
  597. } else {
  598. SquirtSqflPtszV(sqfl, TEXT("%S: Still acquired %d"),
  599. s_szProc, this->cAcquire);
  600. hres = S_OK;
  601. }
  602. } else {
  603. SquirtSqflPtszV(sqfl, TEXT("%S: Not acquired %d"),
  604. s_szProc, this->cAcquire);
  605. hres = S_OK;
  606. }
  607. ExitOleProc();
  608. return hres;
  609. }
  610. /*****************************************************************************
  611. *
  612. * @doc INTERNAL
  613. *
  614. * @func void | CEm_ForceDeviceUnacquire |
  615. *
  616. * Force all users of a device to unacquire.
  617. *
  618. * @parm PEM | pem |
  619. *
  620. * Information about the gizmo being mangled.
  621. *
  622. * @parm UINT | fdufl |
  623. *
  624. * Assorted flags describing why we are being unacquired.
  625. *
  626. *****************************************************************************/
  627. void EXTERNAL
  628. CEm_ForceDeviceUnacquire(PED ped, UINT fdufl)
  629. {
  630. PEM pem, pemNext;
  631. AssertF((fdufl & ~FDUFL_UNPLUGGED) == 0);
  632. AssertF(!DllInCrit());
  633. DllEnterCrit();
  634. for (pem = g_pemFirst; pem; pem = pemNext) {
  635. AssertF(pem->dwSignature == CEM_SIGNATURE);
  636. if (pem->ped == ped && (pem->vi.fl & VIFL_ACQUIRED)) {
  637. CEm_AddRef(pem);
  638. DllLeaveCrit();
  639. CEm_UnacquirePem(pem, fdufl);
  640. CDIDev_SetForcedUnacquiredFlag(pem->vi.pdd);
  641. /*
  642. * Since this happens only when the device is acquired,
  643. * we don't need to worry about the notify event changing
  644. * asynchronously.
  645. */
  646. CDIDev_SetNotifyEvent(pem->vi.pdd);
  647. DllEnterCrit();
  648. pemNext = pem->pemNext;
  649. AssertF(pem->dwSignature == CEM_SIGNATURE);
  650. CEm_Release(pem);
  651. } else {
  652. pemNext = pem->pemNext;
  653. AssertF(pem->dwSignature == CEM_SIGNATURE);
  654. }
  655. }
  656. DllLeaveCrit();
  657. }
  658. /*****************************************************************************
  659. *
  660. * @doc INTERNAL
  661. *
  662. * @func HRESULT | CEm_DestroyInstance |
  663. *
  664. * Clean up an instance.
  665. *
  666. *****************************************************************************/
  667. HRESULT EXTERNAL
  668. CEm_DestroyInstance(PVXDINSTANCE *ppvi)
  669. {
  670. HRESULT hres;
  671. PEM this = _thisPvNm(*ppvi, vi);
  672. EnterProc(CEm_DestroyInstance, (_ "p", *ppvi));
  673. AssertF(this->dwSignature == CEM_SIGNATURE);
  674. AssertF((PV)this == (PV)*ppvi);
  675. if (this) {
  676. CEm_Release(this);
  677. }
  678. hres = S_OK;
  679. ExitOleProc();
  680. return hres;
  681. }
  682. /*****************************************************************************
  683. *
  684. * @doc INTERNAL
  685. *
  686. * @func HRESULT | CEm_SetDataFormat |
  687. *
  688. * Record the application data format in the device so that
  689. * we can translate it for buffering purposes.
  690. *
  691. * @parm PVXDDATAFORMAT | pvdf |
  692. *
  693. * Information about the gizmo being mangled.
  694. *
  695. *****************************************************************************/
  696. HRESULT INTERNAL
  697. CEm_SetDataFormat(PVXDDATAFORMAT pvdf)
  698. {
  699. HRESULT hres;
  700. PEM this = _thisPvNm(pvdf->pvi, vi);
  701. EnterProc(CEm_SetDataFormat, (_ "p", pvdf->pvi));
  702. AssertF(this->dwSignature == CEM_SIGNATURE);
  703. hres = ReallocCbPpv( cbCdw(pvdf->cbData), &this->rgdwDf);
  704. if (SUCCEEDED(hres)) {
  705. AssertF(pvdf->cbData == this->ped->cbData);
  706. memcpy(this->rgdwDf, pvdf->pDfOfs, cbCdw(pvdf->cbData) );
  707. }
  708. ExitOleProc();
  709. return hres;
  710. }
  711. /*****************************************************************************
  712. *
  713. * @doc INTERNAL
  714. *
  715. * @func HRESULT | CEm_AcquireInstance |
  716. *
  717. * Acquire the device in the device-specific way.
  718. *
  719. * @parm PVXDINSTANCE * | ppvi |
  720. *
  721. * The instance to acquire.
  722. *
  723. *****************************************************************************/
  724. HRESULT INTERNAL
  725. CEm_AcquireInstance(PVXDINSTANCE *ppvi)
  726. {
  727. HRESULT hres;
  728. PEM this = _thisPvNm(*ppvi, vi);
  729. #ifdef DEBUG
  730. EnterProc(CEm_AcquireInstance, (_ "p", *ppvi));
  731. #else
  732. EnterProcR(IDirectInputDevice8::Acquire, (_ "p", *ppvi));
  733. #endif
  734. AssertF(this->dwSignature == CEM_SIGNATURE);
  735. this->vi.fl |= VIFL_ACQUIRED;
  736. if (InterlockedIncrement(&this->cAcquire) == 0) {
  737. InterlockedIncrement(&this->ped->cAcquire);
  738. hres = this->ped->Acquire(this, 1);
  739. if (FAILED(hres)) {
  740. this->vi.fl &= ~VIFL_ACQUIRED;
  741. InterlockedDecrement(&this->cAcquire);
  742. }
  743. } else {
  744. SquirtSqflPtszV(sqfl, TEXT("%S: Already acquired %d"),
  745. s_szProc, this->cAcquire);
  746. hres = S_OK;
  747. }
  748. ExitOleProc();
  749. return hres;
  750. }
  751. /*****************************************************************************
  752. *
  753. * @doc INTERNAL
  754. *
  755. * @func HRESULT | CEm_UnacquireInstance |
  756. *
  757. * Unacquire the device in the device-specific way.
  758. *
  759. * @parm PVXDINSTANCE * | ppvi |
  760. *
  761. * Information about the gizmo being mangled.
  762. *
  763. *****************************************************************************/
  764. HRESULT INTERNAL
  765. CEm_UnacquireInstance(PVXDINSTANCE *ppvi)
  766. {
  767. HRESULT hres;
  768. PEM this = _thisPvNm(*ppvi, vi);
  769. EnterProc(CEm_UnacquireInstance, (_ "p", *ppvi));
  770. hres = CEm_UnacquirePem(this, FDUFL_NORMAL);
  771. ExitOleProc();
  772. return hres;
  773. }
  774. /*****************************************************************************
  775. *
  776. * @doc INTERNAL
  777. *
  778. * @func HRESULT | CEm_SetBufferSize |
  779. *
  780. * Allocate a buffer of the appropriate size.
  781. *
  782. * @parm PVXDDWORDDATA | pvdd |
  783. *
  784. * The <p dwData> is the buffer size.
  785. *
  786. *****************************************************************************/
  787. HRESULT INTERNAL
  788. CEm_SetBufferSize(PVXDDWORDDATA pvdd)
  789. {
  790. HRESULT hres;
  791. PEM this = _thisPvNm(pvdd->pvi, vi);
  792. EnterProc(CEm_SetBufferSize, (_ "px", pvdd->pvi, pvdd->dw));
  793. AssertF(this->dwSignature == CEM_SIGNATURE);
  794. hres = ReallocCbPpv(cbCxX(pvdd->dw, DIDEVICEOBJECTDATA),
  795. &this->vi.pBuffer);
  796. if (SUCCEEDED(hres)) {
  797. this->vi.pHead = this->vi.pBuffer;
  798. this->vi.pTail = this->vi.pBuffer;
  799. this->vi.pEnd = &this->vi.pBuffer[pvdd->dw];
  800. }
  801. ExitOleProc();
  802. return hres;
  803. }
  804. #ifdef USE_SLOW_LL_HOOKS
  805. /*****************************************************************************
  806. *
  807. * @struct LLHOOKINFO |
  808. *
  809. * Information about how to install a low-level hook.
  810. *
  811. * @field int | idHook |
  812. *
  813. * The Windows hook identifier.
  814. *
  815. * @field HOOKPROC | hp |
  816. *
  817. * The hook procedure itself.
  818. *
  819. *****************************************************************************/
  820. typedef struct LLHOOKINFO {
  821. int idHook;
  822. HOOKPROC hp;
  823. } LLHOOKINFO, *PLLHOOKINFO;
  824. typedef const LLHOOKINFO *PCLLHOOKINFO;
  825. #pragma BEGIN_CONST_DATA
  826. const LLHOOKINFO c_rgllhi[] = {
  827. { WH_KEYBOARD_LL, CEm_LL_KbdHook }, /* LLTS_KBD */
  828. { WH_MOUSE_LL, CEm_LL_MseHook }, /* LLTS_MSE */
  829. };
  830. #pragma END_CONST_DATA
  831. /*****************************************************************************
  832. *
  833. * @doc INTERNAL
  834. *
  835. * @func void | CEm_LL_SyncHook |
  836. *
  837. * Install or remove a hook as needed.
  838. *
  839. * @parm UINT | ilts |
  840. *
  841. * Which hook is being handled?
  842. *
  843. * @parm PLLTHREADSTATE | plts |
  844. *
  845. * Thread hook state containing hook information to synchronize.
  846. *
  847. *****************************************************************************/
  848. void INTERNAL
  849. CEm_LL_SyncHook(PLLTHREADSTATE plts, UINT ilts)
  850. {
  851. PLLHOOKSTATE plhs = &plts->rglhs[ilts];
  852. if (!fLeqvFF(plhs->cHook, plhs->hhk)) {
  853. if (plhs->hhk) {
  854. UnhookWindowsHookEx(plhs->hhk);
  855. plhs->hhk = 0;
  856. } else {
  857. PCLLHOOKINFO pllhi = &c_rgllhi[ilts];
  858. plhs->hhk = SetWindowsHookEx(pllhi->idHook, pllhi->hp, g_hinst, 0);
  859. }
  860. }
  861. }
  862. #endif /* USE_SLOW_LL_HOOKS */
  863. #ifdef WORKER_THREAD
  864. /*****************************************************************************
  865. *
  866. * @doc INTERNAL
  867. *
  868. * @func DWORD | FakeMsgWaitForMultipleObjectsEx |
  869. *
  870. * Stub function which emulates
  871. * <f MsgWaitForMultipleObjectsEx>
  872. * on platforms that do not support it.
  873. *
  874. * Such platforms (namely, Windows 95) do not support HID
  875. * and therefore the inability to go into an alertable
  876. * wait state constitutes no loss of amenity.
  877. *
  878. * @parm DWORD | nCount |
  879. *
  880. * Number of handles in handle array.
  881. *
  882. * @parm LPHANDLE | pHandles |
  883. *
  884. * Pointer to an object-handle array.
  885. *
  886. * @parm DWORD | ms |
  887. *
  888. * Time-out interval in milliseconds.
  889. *
  890. * @parm DWORD | dwWakeMask |
  891. *
  892. * Type of input events to wait for.
  893. *
  894. * @parm DWORD | dwFlags |
  895. *
  896. * Wait flags.
  897. *
  898. * @returns
  899. *
  900. * Same as <f MsgWaitForMultipleObjectsEx>.
  901. *
  902. *****************************************************************************/
  903. DWORD WINAPI
  904. FakeMsgWaitForMultipleObjectsEx(
  905. DWORD nCount,
  906. LPHANDLE pHandles,
  907. DWORD ms,
  908. DWORD dwWakeMask,
  909. DWORD dwFlags)
  910. {
  911. /*
  912. * We merely call the normal MsgWaitForMultipleObjects because
  913. * the only way we can get here is on a platform that doesn't
  914. * support HID.
  915. */
  916. return MsgWaitForMultipleObjects(nCount, pHandles,
  917. dwFlags & MWMO_WAITALL, ms, dwWakeMask);
  918. }
  919. #ifdef WINNT
  920. // On win2k non-exclusive mode user thinks the Dinput thread is hung.
  921. // In order to fix this we set a TimerEvent and wake up every so
  922. // often and execute the FakeTimerProc. This keeps user happy and
  923. // keeps dinput thread from being marked as hung and we can get
  924. // events to our low level hooks
  925. VOID CALLBACK FakeTimerProc(
  926. HWND hwnd, // handle to window
  927. UINT uMsg, // WM_TIMER message
  928. UINT_PTR idEvent, // timer identifier
  929. DWORD dwTime // current system time
  930. )
  931. {
  932. }
  933. #endif
  934. #ifdef USE_WM_INPUT
  935. #pragma BEGIN_CONST_DATA
  936. TCHAR c_szEmClassName[] = TEXT("DIEmWin");
  937. #pragma END_CONST_DATA
  938. /****************************************************************************
  939. *
  940. * CEm_WndProc
  941. *
  942. * Window procedure for simple sample.
  943. *
  944. ****************************************************************************/
  945. LRESULT CALLBACK
  946. CEm_WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
  947. {
  948. switch (msg) {
  949. //case WM_INPUT:
  950. // RPF("in WM_INPUT message");
  951. // break;
  952. default:
  953. break;
  954. }
  955. return DefWindowProc(hwnd, msg, wParam, lParam);
  956. }
  957. HWND
  958. CEm_InitWindow(void)
  959. {
  960. HWND hwnd;
  961. WNDCLASS wc;
  962. static BOOL fFirstTime = TRUE;
  963. if( fFirstTime ) {
  964. wc.hCursor = LoadCursor(0, IDC_ARROW);
  965. wc.hIcon = LoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION));
  966. wc.lpszMenuName = NULL;
  967. wc.lpszClassName = c_szEmClassName;
  968. wc.hbrBackground = 0;
  969. wc.hInstance = g_hinst;
  970. wc.style = 0;
  971. wc.lpfnWndProc = CEm_WndProc;
  972. wc.cbClsExtra = 0;
  973. wc.cbWndExtra = 0;
  974. if (!RegisterClass(&wc)) {
  975. return NULL;
  976. }
  977. fFirstTime = FALSE;
  978. }
  979. hwnd = CreateWindow(
  980. c_szEmClassName, // Class name
  981. TEXT("DIEmWin"), // Caption
  982. WS_OVERLAPPEDWINDOW, // Style
  983. -1, -1, // Position
  984. 1, 1, // Size
  985. NULL, //parent
  986. NULL, // No menu
  987. g_hinst, // inst handle
  988. 0 // no params
  989. );
  990. if( !hwnd ) {
  991. RPF("CreateWindow failed.");
  992. }
  993. return hwnd;
  994. }
  995. #endif
  996. /*****************************************************************************
  997. *
  998. * @doc INTERNAL
  999. *
  1000. * @func VOID | CEm_LL_ThreadProc |
  1001. *
  1002. * The thread that manages our low-level hooks.
  1003. *
  1004. * ThreadProcs are prototyped to return a DWORD but since the return
  1005. * would follow some form of ExitThread, it will never be reached so
  1006. * this function is declared to return VOID and cast.
  1007. *
  1008. * When we get started, and whenever we receive any message
  1009. * whatsoever, re-check to see which hooks should be installed
  1010. * and re-synchronize ourselves with them.
  1011. *
  1012. * Note that restarting can be slow, since it happens only
  1013. * when we get nudged by a client.
  1014. *
  1015. * @parm PLLTHREADSTATE | plts |
  1016. *
  1017. * The thread state to use.
  1018. *
  1019. *****************************************************************************/
  1020. VOID INTERNAL
  1021. CEm_LL_ThreadProc(PLLTHREADSTATE plts)
  1022. {
  1023. MSG msg;
  1024. DWORD dwRc;
  1025. #ifdef USE_WM_INPUT
  1026. HWND hwnd = NULL;
  1027. #endif
  1028. AssertF(plts->idThread == GetCurrentThreadId());
  1029. SquirtSqflPtszV(sqflLl, TEXT("CEm_LL_ThreadProc: Thread started"));
  1030. #ifdef USE_SLOW_LL_HOOKS
  1031. /*
  1032. * Refresh the mouse acceleration values.
  1033. *
  1034. * ISSUE-2001/03/29-timgill Need a window to listen for WM_SETTINGCHANGE
  1035. * We need to create a window to listen for
  1036. * WM_SETTINGCHANGE so we can refresh the mouse acceleration
  1037. * as needed.
  1038. */
  1039. CEm_Mouse_OnMouseChange();
  1040. #endif
  1041. /*
  1042. * Create ourselves a queue before we go into our "hey what happened
  1043. * before I got here?" phase. The thread that created us is waiting on
  1044. * the thread event, holding DLLCrit, so let it go as soon as the queue
  1045. * is ready. We create the queue by calling a function that requires a
  1046. * queue. We use this very simple one.
  1047. */
  1048. GetInputState();
  1049. #ifdef WINNT
  1050. // Look at comment block in FakeTimerProc
  1051. SetTimer(NULL, 0, 2 * 1000 /*2 seconds*/, FakeTimerProc);
  1052. #endif
  1053. SetEvent(plts->hEvent);
  1054. #ifdef USE_WM_INPUT
  1055. ResetEvent(g_hEventThread);
  1056. if( g_fRawInput ) {
  1057. hwnd = CEm_InitWindow();
  1058. if (!hwnd) {
  1059. g_fRawInput = FALSE;
  1060. }
  1061. }
  1062. g_hwndThread = hwnd;
  1063. // Tell CEm_LL_Acquire that windows has been created.
  1064. SetEvent( g_hEventAcquire );
  1065. if( g_fFromKbdMse ) {
  1066. DWORD rc;
  1067. rc = WaitForSingleObject(g_hEventThread, INFINITE);
  1068. g_fFromKbdMse = FALSE;
  1069. }
  1070. #endif
  1071. #ifdef USE_SLOW_LL_HOOKS
  1072. /*
  1073. * Note carefully that we sync the hooks before entering our
  1074. * fake GetMessage loop. This is necessary to avoid the race
  1075. * condition when CEm_LL_Acquire posts us a thread message
  1076. * before our thread gets a queue. By sync'ing the hooks
  1077. * first, we do what the lost message would've had us do
  1078. * anyway.
  1079. * ISSUE-2001/03/29-timgill Following branch should be no longer necessary
  1080. * This is should not be needed now that CEm_GetWorkerThread waits for
  1081. * this thread to respond before continuing on to post any messages.
  1082. */
  1083. #endif /* USE_SLOW_LL_HOOKS */
  1084. do {
  1085. #ifdef USE_SLOW_LL_HOOKS
  1086. if( !g_fRawInput ) {
  1087. CEm_LL_SyncHook(plts, LLTS_KBD);
  1088. CEm_LL_SyncHook(plts, LLTS_MSE);
  1089. }
  1090. #endif
  1091. /*
  1092. * We can wake up for three reasons.
  1093. *
  1094. * 1. We received an APC due to an I/o completion.
  1095. * Just go back to sleep.
  1096. *
  1097. * 2. We need to call Peek/GetMessage so that
  1098. * USER can dispatch a low-level hook or SendMessage.
  1099. * Go into a PeekMessage loop to let that happen.
  1100. *
  1101. * 3. A message got posted to us.
  1102. * Go into a PeekMessage loop to process it.
  1103. */
  1104. do {
  1105. dwRc = _MsgWaitForMultipleObjectsEx(0, 0, INFINITE, QS_ALLINPUT,
  1106. MWMO_ALERTABLE);
  1107. } while (dwRc == WAIT_IO_COMPLETION);
  1108. while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
  1109. if (msg.hwnd == 0 && msg.message == WM_NULL && msg.lParam)
  1110. {
  1111. /*
  1112. * See if maybe the lParam is a valid PEM that we're
  1113. * processing.
  1114. */
  1115. PEM pem = (PEM)msg.lParam;
  1116. if( pem && pem == plts->pemCheck )
  1117. {
  1118. AssertF(GPA_FindPtr(&plts->gpaHid, pem));
  1119. CEm_HID_Sync(plts, pem);
  1120. plts->pemCheck = NULL;
  1121. SetEvent(plts->hEvent);
  1122. #ifdef USE_WM_INPUT
  1123. if( g_fRawInput ) {
  1124. SetEvent(g_hEventHid);
  1125. }
  1126. #endif
  1127. continue;
  1128. }
  1129. }
  1130. #ifdef USE_WM_INPUT
  1131. else if ( g_fRawInput && msg.message == WM_INPUT &&
  1132. (msg.wParam == RIM_INPUT || msg.wParam == RIM_INPUTSINK) )
  1133. {
  1134. CDIRaw_OnInput(&msg);
  1135. }
  1136. #endif
  1137. TranslateMessage(&msg);
  1138. DispatchMessage(&msg);
  1139. }
  1140. } while (plts->cRef);
  1141. #ifdef USE_SLOW_LL_HOOKS
  1142. /*
  1143. * Remove our hooks before we go.
  1144. *
  1145. * It is possible that there was a huge flurry of disconnects,
  1146. * causing us to notice that our refcount disappeared before
  1147. * we got a chance to remove the hooks in our message loop.
  1148. */
  1149. AssertF(plts->rglhs[LLTS_KBD].cHook == 0);
  1150. AssertF(plts->rglhs[LLTS_KBD].cExcl == 0);
  1151. AssertF(plts->rglhs[LLTS_MSE].cHook == 0);
  1152. AssertF(plts->rglhs[LLTS_MSE].cExcl == 0);
  1153. if( !g_fRawInput ) {
  1154. if (plts->rglhs[LLTS_KBD].hhk) {
  1155. UnhookWindowsHookEx(plts->rglhs[LLTS_KBD].hhk);
  1156. }
  1157. if (plts->rglhs[LLTS_MSE].hhk) {
  1158. UnhookWindowsHookEx(plts->rglhs[LLTS_MSE].hhk);
  1159. }
  1160. }
  1161. #endif /* USE_SLOW_LL_HOOKS */
  1162. #ifdef USE_WM_INPUT
  1163. if( g_hwndThread ) {
  1164. DestroyWindow( g_hwndThread );
  1165. g_hwndThread = NULL;
  1166. }
  1167. ResetEvent( g_hEventAcquire );
  1168. ResetEvent( g_hEventHid );
  1169. #endif
  1170. if( plts->gpaHid.rgpv ) {
  1171. FreePpv(&plts->gpaHid.rgpv);
  1172. }
  1173. if( plts->hEvent ) {
  1174. CloseHandle( plts->hEvent );
  1175. }
  1176. if( plts->hThread) {
  1177. CloseHandle(plts->hThread);
  1178. }
  1179. FreePpv( &plts );
  1180. SquirtSqflPtszV(sqflLl, TEXT("CEm_LL_ThreadProc: Thread terminating"));
  1181. FreeLibraryAndExitThread(g_hinst, 0);
  1182. /*NOTREACHED*/
  1183. }
  1184. /*****************************************************************************
  1185. *
  1186. * @doc INTERNAL
  1187. *
  1188. * @func HRESULT | CEm_GetWorkerThread |
  1189. *
  1190. * Piggyback off the existing worker thread if possible;
  1191. * else create a new one.
  1192. *
  1193. * @parm PEM | pem |
  1194. *
  1195. * Emulation state which requires a worker thread.
  1196. *
  1197. * @parm PLLTHREADSTATE * | pplts |
  1198. *
  1199. * Receives thread state for worker thread.
  1200. *
  1201. *****************************************************************************/
  1202. STDMETHODIMP
  1203. CEm_GetWorkerThread(PEM pem, PLLTHREADSTATE *pplts)
  1204. {
  1205. PLLTHREADSTATE plts;
  1206. HRESULT hres;
  1207. DllEnterCrit();
  1208. /*
  1209. * Normally, we can piggyback off the one we already have.
  1210. */
  1211. plts = g_plts;
  1212. /*
  1213. * If we already have a ref to a worker thread, then use it.
  1214. */
  1215. if (pem->fWorkerThread) {
  1216. /*
  1217. * The reference we created when we created the worker thread
  1218. * ensures that g_plts is valid.
  1219. */
  1220. AssertF(plts);
  1221. AssertF(plts->cRef);
  1222. if (plts) {
  1223. hres = S_OK;
  1224. } else {
  1225. AssertF(0); /* Can't happen */
  1226. hres = E_FAIL;
  1227. }
  1228. } else
  1229. if (plts) {
  1230. /*
  1231. * Create a reference to the existing thread.
  1232. */
  1233. pem->fWorkerThread = TRUE;
  1234. InterlockedIncrement(&plts->cRef);
  1235. hres = S_OK;
  1236. } else {
  1237. /*
  1238. * There is no worker thread (or it is irretrievably
  1239. * on its way out) so create a new one.
  1240. */
  1241. hres = AllocCbPpv(cbX(LLTHREADSTATE), &plts);
  1242. if (SUCCEEDED(hres)) {
  1243. DWORD dwRc = 0;
  1244. TCHAR tsz[MAX_PATH];
  1245. /*
  1246. * Assume the worst unless we find otherwise
  1247. */
  1248. hres = E_FAIL;
  1249. if( GetModuleFileName(g_hinst, tsz, cA(tsz))
  1250. && ( LoadLibrary(tsz) == g_hinst ) )
  1251. {
  1252. /*
  1253. * Must set up everything to avoid racing with
  1254. * the incoming thread.
  1255. */
  1256. g_plts = plts;
  1257. InterlockedIncrement(&plts->cRef);
  1258. plts->hEvent = CreateEvent(0x0, 0, 0, 0x0);
  1259. if( plts->hEvent )
  1260. {
  1261. plts->hThread= CreateThread(0, 0, (LPTHREAD_START_ROUTINE)CEm_LL_ThreadProc, plts,
  1262. 0, &plts->idThread);
  1263. if( plts->hThread )
  1264. {
  1265. /*
  1266. * Boost our priority to make sure we
  1267. * can handle the messages.
  1268. *
  1269. * RaymondC commented this out saying that it does not
  1270. * help but we're hoping that it may on Win2k.
  1271. */
  1272. SetThreadPriority(plts->hThread, THREAD_PRIORITY_HIGHEST);
  1273. /*
  1274. * Wait for the thread to signal that it is up and running
  1275. * or for it to terminate.
  1276. * This means that we don't have to consider the
  1277. * possibility that the thread is not yet running in
  1278. * NotifyWorkerThreadPem so we know a failure there is
  1279. * terminal and don't retry.
  1280. *
  1281. * Assert that the handle fields make a two handle array.
  1282. */
  1283. CAssertF( FIELD_OFFSET( LLTHREADSTATE, hThread) + sizeof(plts->hThread)
  1284. == FIELD_OFFSET( LLTHREADSTATE, hEvent) );
  1285. /*
  1286. * According to a comment in CEm_LL_ThreadProc Win95 may
  1287. * fail with an invalid parameter error, so if it does,
  1288. * keep trying. (Assume no valid case will occur.)
  1289. *
  1290. * ISSUE-2001/03/29-timgill Need to minimise waits while holding sync. objects
  1291. * Waiting whilst holding DLLCrit is bad.
  1292. */
  1293. do
  1294. {
  1295. dwRc = WaitForMultipleObjects( 2, &plts->hThread, FALSE, INFINITE);
  1296. } while ( ( dwRc == WAIT_FAILED ) && ( GetLastError() == ERROR_INVALID_PARAMETER ) );
  1297. if( dwRc == WAIT_OBJECT_0 ) {
  1298. SquirtSqflPtszV(sqfl | sqflError,
  1299. TEXT("CEm_GetWorkerThread: Created Thread terminated on first wait") );
  1300. } else {
  1301. pem->fWorkerThread = TRUE;
  1302. hres = S_OK;
  1303. if( dwRc != WAIT_OBJECT_0 + 1 )
  1304. {
  1305. /*
  1306. * This would be a bad thing if it ever happened
  1307. * but we have to assume that the thread is still
  1308. * running so we return a success anyway.
  1309. */
  1310. SquirtSqflPtszV(sqfl | sqflError,
  1311. TEXT("CEm_GetWorkerThread: First wait returned 0x%08x with LastError %d"),
  1312. dwRc, GetLastError() );
  1313. }
  1314. }
  1315. }
  1316. else
  1317. {
  1318. SquirtSqflPtszV(sqfl | sqflError,
  1319. TEXT("CEm_GetWorkerThread: CreateThread failed with error %d"),
  1320. GetLastError() );
  1321. }
  1322. }
  1323. else
  1324. {
  1325. SquirtSqflPtszV(sqfl | sqflError,
  1326. TEXT("CEm_GetWorkerThread: CreateEvent failed with error %d"),
  1327. GetLastError() );
  1328. }
  1329. if( FAILED( hres ) )
  1330. {
  1331. if( plts->hEvent ) {
  1332. CloseHandle( plts->hEvent );
  1333. }
  1334. FreeLibrary(g_hinst);
  1335. }
  1336. }
  1337. else
  1338. {
  1339. RPF( "CEm_GetWorkerThread: failed to LoadLibrary( self ), le = %d", GetLastError() );
  1340. }
  1341. if( FAILED( hres ) )
  1342. {
  1343. FreePv(plts);
  1344. g_plts = 0;
  1345. }
  1346. }
  1347. }
  1348. DllLeaveCrit();
  1349. *pplts = plts;
  1350. return hres;
  1351. }
  1352. #endif /* WORKER_THREAD */
  1353. #ifdef USE_SLOW_LL_HOOKS
  1354. /*****************************************************************************
  1355. *
  1356. * @doc INTERNAL
  1357. *
  1358. * @func HRESULT | CEm_LL_Acquire |
  1359. *
  1360. * Acquire/unacquire a mouse or keyboard via low-level hooks.
  1361. *
  1362. * @parm PEM | pem |
  1363. *
  1364. * Device being acquired.
  1365. *
  1366. * @parm BOOL | fAcquire |
  1367. *
  1368. * Whether the device is being acquired or unacquired.
  1369. *
  1370. * @parm ULONG | fl |
  1371. *
  1372. * Flags in VXDINSTANCE (vi.fl).
  1373. *
  1374. * @parm UINT | ilts |
  1375. *
  1376. * LLTS_KBD or LLTS_MSE, depending on which is happening.
  1377. *
  1378. *****************************************************************************/
  1379. STDMETHODIMP
  1380. CEm_LL_Acquire(PEM this, BOOL fAcquire, ULONG fl, UINT ilts)
  1381. {
  1382. PLLTHREADSTATE plts;
  1383. BOOL fExclusive = fl & VIFL_CAPTURED;
  1384. BOOL fNoWinkey = fl & VIFL_NOWINKEY;
  1385. HRESULT hres = S_OK;
  1386. EnterProc(CEm_LL_Acquire, (_ "puuu", this, fAcquire, fExclusive, ilts));
  1387. AssertF(this->dwSignature == CEM_SIGNATURE);
  1388. AssertF(ilts==LLTS_KBD || ilts==LLTS_MSE);
  1389. #ifdef USE_WM_INPUT
  1390. g_fFromKbdMse = fAcquire ? TRUE : FALSE;
  1391. ResetEvent( g_hEventAcquire );
  1392. #endif
  1393. hres = CEm_GetWorkerThread(this, &plts);
  1394. if (SUCCEEDED(hres)) {
  1395. AssertF( plts->rglhs[ilts].cHook >= plts->rglhs[ilts].cExcl );
  1396. #ifdef USE_WM_INPUT
  1397. if( g_fRawInput && !g_hwndThread) {
  1398. DWORD dwRc;
  1399. dwRc = WaitForSingleObject(g_hEventAcquire, INFINITE);
  1400. }
  1401. #endif
  1402. if (fAcquire) {
  1403. InterlockedIncrement(&plts->rglhs[ilts].cHook);
  1404. if (fExclusive) {
  1405. InterlockedIncrement(&plts->rglhs[ilts].cExcl);
  1406. }
  1407. #ifdef USE_WM_INPUT
  1408. if( g_hwndThread ) {
  1409. if( fExclusive ) {
  1410. hres = CDIRaw_RegisterRawInputDevice(1-ilts, DIRAW_EXCL, g_hwndThread);
  1411. }
  1412. else if( fNoWinkey ) {
  1413. AssertF( ilts == 0 );
  1414. if( ilts == 0 ) {
  1415. hres = CDIRaw_RegisterRawInputDevice(1-ilts, DIRAW_NOHOTKEYS, g_hwndThread);
  1416. } else {
  1417. hres = E_FAIL;
  1418. }
  1419. }
  1420. else {
  1421. hres = CDIRaw_RegisterRawInputDevice(1-ilts, DIRAW_NONEXCL, g_hwndThread);
  1422. }
  1423. if(FAILED(hres)) {
  1424. hres = S_FALSE;
  1425. g_fRawInput = FALSE;
  1426. RPF("CEm_LL_Acquire: RegisterRawInput failed. LL will be used.");
  1427. }
  1428. }
  1429. #endif
  1430. } else { /* Remove the hook */
  1431. AssertF(plts->cRef);
  1432. if (fExclusive) {
  1433. InterlockedDecrement(&plts->rglhs[ilts].cExcl);
  1434. }
  1435. InterlockedDecrement(&plts->rglhs[ilts].cHook);
  1436. #ifdef USE_WM_INPUT
  1437. if( g_fRawInput ) {
  1438. CDIRaw_UnregisterRawInputDevice(1-ilts, g_hwndThread);
  1439. if( plts->rglhs[ilts].cHook ) {
  1440. CDIRaw_RegisterRawInputDevice(1-ilts, 0, g_hwndThread);
  1441. }
  1442. }
  1443. #endif
  1444. }
  1445. NudgeWorkerThread(plts->idThread);
  1446. #ifdef USE_WM_INPUT
  1447. // tell CEm_LL_ThreadProc that acquire finished.
  1448. SetEvent( g_hEventThread );
  1449. #endif
  1450. }
  1451. ExitOleProc();
  1452. return hres;
  1453. }
  1454. #endif /* USE_SLOW_LL_HOOKS */
  1455. /*****************************************************************************
  1456. *
  1457. * Joystick emulation
  1458. *
  1459. *****************************************************************************/
  1460. /*****************************************************************************
  1461. *
  1462. * @doc INTERNAL
  1463. *
  1464. * @func HRESULT | CEm_Joy_Acquire |
  1465. *
  1466. * Acquire a joystick. Nothing happens.
  1467. *
  1468. * @parm PEM | pem |
  1469. *
  1470. * Device being acquired.
  1471. *
  1472. *****************************************************************************/
  1473. STDMETHODIMP
  1474. CEm_Joy_Acquire(PEM this, BOOL fAcquire)
  1475. {
  1476. AssertF(this->dwSignature == CEM_SIGNATURE);
  1477. return S_OK;
  1478. }
  1479. /*****************************************************************************
  1480. *
  1481. * Joystick globals
  1482. *
  1483. * Since we don't use joystick emulation by default, we allocate
  1484. * the emulation variables dynamically so we don't blow a page
  1485. * of memory on them.
  1486. *
  1487. *****************************************************************************/
  1488. typedef struct JOYEMVARS {
  1489. ED rged[cJoyMax];
  1490. DIJOYSTATE2 rgjs2[cJoyMax];
  1491. } JOYEMVARS, *PJOYEMVARS;
  1492. static PJOYEMVARS s_pjev;
  1493. /*****************************************************************************
  1494. *
  1495. * @doc INTERNAL
  1496. *
  1497. * @func HRESULT | CEm_Joy_CreateInstance |
  1498. *
  1499. * Create a joystick thing.
  1500. *
  1501. * @parm PVXDDEVICEFORMAT | pdevf |
  1502. *
  1503. * What the object should look like.
  1504. *
  1505. * @parm PVXDINSTANCE * | ppviOut |
  1506. *
  1507. * The answer goes here.
  1508. *
  1509. *****************************************************************************/
  1510. #define OBJAT(T, v) (*(T *)(v))
  1511. #define PUN(T, v) OBJAT(T, &(v))
  1512. HRESULT INTERNAL
  1513. CEm_Joy_CreateInstance(PVXDDEVICEFORMAT pdevf, PVXDINSTANCE *ppviOut)
  1514. {
  1515. HRESULT hres;
  1516. DllEnterCrit();
  1517. if (s_pjev == 0) {
  1518. DWORD uiJoy;
  1519. hres = AllocCbPpv(cbX(JOYEMVARS), &s_pjev);
  1520. if (SUCCEEDED(hres)) {
  1521. for (uiJoy = 0; uiJoy < cJoyMax; uiJoy++) {
  1522. PUN(PV, s_pjev->rged[uiJoy].pState) = &s_pjev->rgjs2[uiJoy];
  1523. s_pjev->rged[uiJoy].Acquire = CEm_Joy_Acquire;
  1524. s_pjev->rged[uiJoy].cbData = cbX(s_pjev->rgjs2[uiJoy]);
  1525. s_pjev->rged[uiJoy].cRef = 0x0;
  1526. }
  1527. }
  1528. } else {
  1529. hres = S_OK;
  1530. }
  1531. DllLeaveCrit();
  1532. if (SUCCEEDED(hres)) {
  1533. hres = CEm_CreateInstance(pdevf, ppviOut,
  1534. &s_pjev->rged[pdevf->dwExtra]);
  1535. }
  1536. return hres;
  1537. }
  1538. /*****************************************************************************
  1539. *
  1540. * @doc INTERNAL
  1541. *
  1542. * @func HRESULT | CEm_Joy_Ping |
  1543. *
  1544. * Read data from the joystick.
  1545. *
  1546. * @parm PVXDINSTANCE * | ppvi |
  1547. *
  1548. * Information about the gizmo being mangled.
  1549. *
  1550. *****************************************************************************/
  1551. HRESULT INTERNAL
  1552. CEm_Joy_Ping(PVXDINSTANCE *ppvi)
  1553. {
  1554. HRESULT hres;
  1555. JOYINFOEX ji;
  1556. MMRESULT mmrc;
  1557. PEM this = _thisPvNm(*ppvi, vi);
  1558. AssertF(this->dwSignature == CEM_SIGNATURE);
  1559. ji.dwSize = cbX(ji);
  1560. ji.dwFlags = JOY_RETURNALL + JOY_RETURNRAWDATA;
  1561. ji.dwPOV = JOY_POVCENTERED; /* joyGetPosEx forgets to set this */
  1562. mmrc = joyGetPosEx((DWORD)(UINT_PTR)this->dwExtra, &ji);
  1563. if (mmrc == JOYERR_NOERROR) {
  1564. DIJOYSTATE2 js;
  1565. UINT uiButtons;
  1566. ZeroX(js); /* Wipe out the bogus things */
  1567. js.lX = ji.dwXpos;
  1568. js.lY = ji.dwYpos;
  1569. js.lZ = ji.dwZpos;
  1570. js.lRz = ji.dwRpos;
  1571. js.rglSlider[0] = ji.dwUpos;
  1572. js.rglSlider[1] = ji.dwVpos;
  1573. js.rgdwPOV[0] = ji.dwPOV;
  1574. js.rgdwPOV[1] = JOY_POVCENTERED;
  1575. js.rgdwPOV[2] = JOY_POVCENTERED;
  1576. js.rgdwPOV[3] = JOY_POVCENTERED;
  1577. for (uiButtons = 0; uiButtons < 32; uiButtons++) {
  1578. if (ji.dwButtons & (1 << uiButtons)) {
  1579. js.rgbButtons[uiButtons] = 0x80;
  1580. }
  1581. }
  1582. CEm_AddState(&s_pjev->rged[this->dwExtra], &js, GetTickCount());
  1583. hres = S_OK;
  1584. } else {
  1585. /*
  1586. * dinput.dll forces the device unacquired here
  1587. * in DX8 we just return an error
  1588. */
  1589. hres = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32,
  1590. ERROR_DEV_NOT_EXIST);
  1591. }
  1592. return hres;
  1593. }
  1594. HRESULT EXTERNAL
  1595. NotifyWorkerThreadPem(DWORD idThread, PEM pem)
  1596. {
  1597. PLLTHREADSTATE plts;
  1598. HRESULT hres;
  1599. hres = CEm_GetWorkerThread(pem, &plts);
  1600. if( SUCCEEDED(hres) )
  1601. {
  1602. AssertF(plts->idThread == idThread);
  1603. hres = NudgeWorkerThreadPem( plts, pem );
  1604. }
  1605. return hres;
  1606. }
  1607. HRESULT EXTERNAL
  1608. NudgeWorkerThreadPem( PLLTHREADSTATE plts, PEM pem )
  1609. {
  1610. HRESULT hres = S_FALSE;
  1611. plts->pemCheck = pem;
  1612. if( !PostWorkerMessage(plts->idThread, pem))
  1613. {
  1614. SquirtSqflPtszV(sqfl | sqflBenign,
  1615. TEXT("NudgeWorkerThreadPem: PostThreadMessage Failed with error %d"),
  1616. GetLastError() );
  1617. }
  1618. else if( pem )
  1619. {
  1620. DWORD dwRc;
  1621. SquirtSqflPtszV(sqfl | sqflVerbose,
  1622. TEXT("NudgeWorkerThreadPem: PostThreadMessage SUCCEEDED, waiting for event ... "));
  1623. /*
  1624. * According to a comment in CEm_LL_ThreadProc Win95 may
  1625. * fail with an invalid parameter error, so if it does,
  1626. * keep trying. (Assume no valid case will occur.)
  1627. */
  1628. do
  1629. {
  1630. dwRc = WaitForMultipleObjects( 2, &plts->hThread, FALSE, INFINITE);
  1631. } while ( ( dwRc == WAIT_FAILED ) && ( GetLastError() == ERROR_INVALID_PARAMETER ) );
  1632. switch( dwRc )
  1633. {
  1634. case WAIT_OBJECT_0:
  1635. SquirtSqflPtszV(sqfl | sqflBenign,
  1636. TEXT("NotifyWorkerThreadPem: Not expecting response from dead worker thread") );
  1637. break;
  1638. case WAIT_OBJECT_0 + 1:
  1639. /*
  1640. * The worker thread responded OK
  1641. */
  1642. hres = S_OK;
  1643. AssertF(plts->pemCheck == NULL );
  1644. break;
  1645. default:
  1646. SquirtSqflPtszV(sqfl | sqflError,
  1647. TEXT("NotifyWorkerThreadPem: WaitForMultipleObjects returned 0x%08x with LastError %d"),
  1648. dwRc, GetLastError() );
  1649. hres = E_FAIL;
  1650. break;
  1651. }
  1652. }
  1653. return hres;
  1654. }