Source code of Windows XP (NT5)
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.

728 lines
21 KiB

  1. /*****************************************************************************
  2. *
  3. * DICal.c
  4. *
  5. * Copyright (c) 1996 Microsoft Corporation. All Rights Reserved.
  6. *
  7. * Abstract:
  8. *
  9. * Functions that manage axis ramps and calibration.
  10. *
  11. * Structure names begin with "Joy" for historical reasons.
  12. *
  13. * Contents:
  14. *
  15. * CCal_CookRange
  16. * CCal_RecalcRange
  17. *
  18. *****************************************************************************/
  19. #include "dinputpr.h"
  20. /*****************************************************************************
  21. *
  22. * The sqiffle for this file.
  23. *
  24. *****************************************************************************/
  25. #define sqfl sqflCal
  26. /*****************************************************************************
  27. *
  28. * @doc INTERNAL
  29. *
  30. * @func LONG | CCal_MulDiv |
  31. *
  32. * High-speed MulDiv for Intel x86 boxes. Otherwise, uses
  33. * the standard MulDiv. The values involved are always
  34. * nonnegative.
  35. *
  36. * @parm LONG | lA |
  37. *
  38. * Multiplicand.
  39. *
  40. * @parm LONG | lB |
  41. *
  42. * Multiplier.
  43. *
  44. * @parm LONG | lC |
  45. *
  46. * Denominator.
  47. *
  48. * @returns
  49. *
  50. * lA * lB / lC, with 64-bit intermediate precision.
  51. *
  52. *****************************************************************************/
  53. #if defined(_X86_)
  54. #pragma warning(disable:4035) /* no return value (duh) */
  55. __declspec(naked) LONG EXTERNAL
  56. CCal_MulDiv(LONG lA, LONG lB, LONG lC)
  57. {
  58. lA; lB; lC;
  59. _asm {
  60. mov eax, [esp+4]
  61. mul dword ptr [esp+8]
  62. div dword ptr [esp+12]
  63. ret 12
  64. }
  65. }
  66. #pragma warning(default:4035)
  67. #endif
  68. /*****************************************************************************
  69. *
  70. * @doc INTERNAL
  71. *
  72. * @method void | CCal | CookAxisPOV |
  73. *
  74. * Cook a piece of POV data into one of five defined data.
  75. *
  76. * @cwrap PJOYRANGECONVERT | this
  77. *
  78. * @parm INOUT PLONG | pl |
  79. *
  80. * On entry, contains the raw value. On exit, contains the
  81. * cooked value. (Or the raw value if the axis is raw.)
  82. *
  83. * @returns
  84. *
  85. * None.
  86. *
  87. *****************************************************************************/
  88. #ifdef WINNT
  89. void CookAxisPOV( PJOYRANGECONVERT this, LONG UNALIGNED *pl )
  90. {
  91. LONG l;
  92. /*
  93. * figure out which direction this value indicates...
  94. */
  95. if( (*pl > this->lMinPOV[JOY_POVVAL_FORWARD])
  96. &&(*pl < this->lMaxPOV[JOY_POVVAL_FORWARD]) )
  97. {
  98. l = JOY_POVFORWARD;
  99. }
  100. else if( (*pl > this->lMinPOV[JOY_POVVAL_BACKWARD])
  101. &&(*pl < this->lMaxPOV[JOY_POVVAL_BACKWARD]) )
  102. {
  103. l = JOY_POVBACKWARD;
  104. }
  105. else if( (*pl > this->lMinPOV[JOY_POVVAL_LEFT])
  106. &&(*pl < this->lMaxPOV[JOY_POVVAL_LEFT]) )
  107. {
  108. l = JOY_POVLEFT;
  109. }
  110. else if( (*pl > this->lMinPOV[JOY_POVVAL_RIGHT])
  111. &&(*pl < this->lMaxPOV[JOY_POVVAL_RIGHT]) )
  112. {
  113. l = JOY_POVRIGHT;
  114. }
  115. else
  116. {
  117. l = JOY_POVCENTERED;
  118. }
  119. #if 0
  120. {
  121. TCHAR buf[100];
  122. wsprintf(buf, TEXT("calibrated pov: %d\r\n"), l);
  123. OutputDebugString(buf);
  124. }
  125. #endif
  126. *pl = l;
  127. }
  128. #endif
  129. /*****************************************************************************
  130. *
  131. * @doc INTERNAL
  132. *
  133. * @method void | CCal | CookRange |
  134. *
  135. * Cook a piece of phys data into a range.
  136. *
  137. * @cwrap PJOYRANGECONVERT | this
  138. *
  139. * @parm INOUT PLONG | pl |
  140. *
  141. * On entry, contains the raw value. On exit, contains the
  142. * cooked value. (Or the raw value if the axis is raw.)
  143. *
  144. * @returns
  145. *
  146. * None.
  147. *
  148. *****************************************************************************/
  149. void EXTERNAL
  150. CCal_CookRange(PJOYRANGECONVERT this, LONG UNALIGNED *pl)
  151. {
  152. if (this->fRaw) {
  153. /*
  154. * Nothing to do!
  155. */
  156. } else {
  157. #ifdef WINNT
  158. if( this->fPolledPOV ) {
  159. CookAxisPOV( this, pl );
  160. } else
  161. #endif
  162. {
  163. LONG lRc = 0;
  164. LONG l;
  165. PCJOYRAMP prmp;
  166. l = *pl;
  167. /*
  168. * Choose the low or high ramp, depending on which side we're in.
  169. *
  170. * This comparison could've been against Dmax or Dmin or Pc.
  171. * We must use Dmax because we jiggered up the rmpHigh so
  172. * that it rounds properly, so we can't use the flat part
  173. * below rmpHigh.x because it's at the wrong level.
  174. */
  175. if (l < this->rmpHigh.x) {
  176. prmp = &this->rmpLow;
  177. } else {
  178. prmp = &this->rmpHigh;
  179. }
  180. if (l <= prmp->x) {
  181. lRc = 0;
  182. } else {
  183. l -= prmp->x;
  184. if ((DWORD)l < prmp->dx) {
  185. /*
  186. * Note that prmp->dx cannot be zero because it
  187. * is greater than something!
  188. */
  189. lRc = CCal_MulDiv((DWORD)l, prmp->dy, prmp->dx);
  190. } else {
  191. lRc = prmp->dy;
  192. }
  193. }
  194. lRc += prmp->y;
  195. if( this->dwCPointsNum > 2 )
  196. {
  197. LONG l2 = *pl;
  198. BOOL fCooked = FALSE;
  199. DWORD i;
  200. if( l2 < this->rmpLow.x || l2 > (this->rmpHigh.x + (LONG)this->rmpHigh.dx) || //in Saturation Zone
  201. ( l2 > (this->rmpLow.x + (LONG)this->rmpLow.dx) && l2 < this->rmpHigh.x ) //in Dead Zone
  202. ) {
  203. //RPF( "Raw: %d Cooked: %ld in Saturation or Dead Zone." );
  204. goto _exitcp;
  205. }
  206. for(i=0; i<this->dwCPointsNum-1; i++) {
  207. if( l2 >= this->cp[i].lP && l2 < this->cp[i+1].lP ) {
  208. l2 -= this->cp[i].lP;
  209. if( this->cp[i+1].dwLog > this->cp[i].dwLog ) {
  210. lRc = CCal_MulDiv((DWORD)l2,
  211. this->cp[i+1].dwLog - this->cp[i].dwLog,
  212. this->cp[i+1].lP - this->cp[i].lP);
  213. } else {
  214. lRc = -1 * CCal_MulDiv((DWORD)l2,
  215. this->cp[i].dwLog - this->cp[i+1].dwLog,
  216. this->cp[i+1].lP - this->cp[i].lP);
  217. }
  218. lRc += this->cp[i].dwLog;
  219. AssertF(lRc >= 0);
  220. AssertF(this->lMax >= this->lMin);
  221. lRc = CCal_MulDiv((DWORD)lRc,
  222. this->lMax - this->lMin + 1,
  223. RANGEDIVISIONS);
  224. lRc += this->lMin;
  225. fCooked = TRUE;
  226. #if 0
  227. RPF( "Raw: %d Cooked: %ld Area %d: (%d - %d) -> (%d - %d)",
  228. *pl, lRc, i, this->cp[i].lP, this->cp[i+1].lP,
  229. this->cp[i].dwLog, this->cp[i+1].dwLog );
  230. #endif
  231. break;
  232. }
  233. }
  234. _exitcp:
  235. ;
  236. }
  237. *pl = lRc;
  238. }
  239. }
  240. }
  241. /*****************************************************************************
  242. *
  243. * @doc INTERNAL
  244. *
  245. * @method void | CCal | RecalcRange |
  246. *
  247. * Compute all the values that derive from the user's
  248. * range settings.
  249. *
  250. * Be careful not to create values that will cause us to
  251. * divide by zero later. Fortunately,
  252. * <f CCal_CookRange> never divides by zero due to the
  253. * clever way it was written.
  254. *
  255. * @cwrap PJOYRANGECONVERT | this
  256. *
  257. * @returns
  258. *
  259. * None.
  260. *
  261. *****************************************************************************/
  262. void EXTERNAL
  263. CCal_RecalcRange(PJOYRANGECONVERT this)
  264. {
  265. int dx;
  266. DWORD dwSat;
  267. AssertF(this->dwDz <= RANGEDIVISIONS);
  268. AssertF(this->dwSat <= RANGEDIVISIONS);
  269. AssertF(this->lMin <= this->lC);
  270. AssertF(this->lC <= this->lMax);
  271. dwSat = max(this->dwSat, this->dwDz);
  272. /* Smin - Bottom of saturation range */
  273. dx = CCal_MulDiv(this->dwPc - this->dwPmin, dwSat, RANGEDIVISIONS);
  274. this->rmpLow.x = this->dwPc - dx;
  275. /* Dmin - Bottom of dead zone */
  276. dx = CCal_MulDiv(this->dwPc - this->dwPmin, this->dwDz, RANGEDIVISIONS);
  277. this->rmpLow.dx = (this->dwPc - dx) - this->rmpLow.x;
  278. /*
  279. * Establish the vertical extent of the low end of the ramp.
  280. */
  281. this->rmpLow.y = this->lMin;
  282. this->rmpLow.dy = this->lC - this->lMin;
  283. /* Dmax - Top of the dead zone */
  284. dx = CCal_MulDiv(this->dwPmax - this->dwPc, this->dwDz, RANGEDIVISIONS);
  285. if (this->dwPmax > this->dwPc+1){
  286. this->rmpHigh.x = this->dwPc + dx + 1;
  287. } else {
  288. this->rmpHigh.x = this->dwPc + dx;
  289. RPF("dwPmax == dwPc (%d). Possible a bug.", this->dwPmax);
  290. }
  291. /* Smax - Top of the saturation range */
  292. dx = CCal_MulDiv(this->dwPmax - this->dwPc, dwSat, RANGEDIVISIONS);
  293. this->rmpHigh.dx = (this->dwPc + dx) - this->rmpHigh.x;
  294. /*
  295. * Establish the vertical extent of the high end of the ramp.
  296. *
  297. * If the high end is zero, then the entire ramp is zero.
  298. * Otherwise, put the bottom at +1 so that when the user
  299. * just barely leaves the dead zone, we report a nonzero
  300. * value. Note: If we were really clever, we could use
  301. * a bias to get "round upwards", but it's not worth it.
  302. *
  303. */
  304. if ( (this->lMax > this->lC) && (this->dwPmax > this->dwPc+1) ) {
  305. this->rmpHigh.y = this->lC + 1;
  306. } else {
  307. this->rmpHigh.y = this->lC;
  308. }
  309. this->rmpHigh.dy = this->lMax - this->rmpHigh.y;
  310. #if 0
  311. RPF( "Raw: %d Dead Zone: 0x%08x Saturation: 0x%08x",
  312. this->fRaw, this->dwDz, this->dwSat );
  313. RPF( "Physical min: 0x%08x max: 0x%08x cen: 0x%08x",
  314. this->lMin, this->lMax, this->lC );
  315. RPF( "Logical min: 0x%08x max: 0x%08x cen: 0x%08x",
  316. this->dwPmin, this->dwPmax, this->dwPc );
  317. RPF( "Lo ramp X: 0x%08x dX: 0x%08x Y: 0x%08x dY: 0x%08x",
  318. this->rmpLow.x, this->rmpLow.dx, this->rmpLow.y, this->rmpLow.dy );
  319. RPF( "Hi ramp X: 0x%08x dX: 0x%08x Y: 0x%08x dY: 0x%08x",
  320. this->rmpHigh.x, this->rmpHigh.dx, this->rmpHigh.y, this->rmpHigh.dy );
  321. #endif
  322. }
  323. /*****************************************************************************
  324. *
  325. * @doc INTERNAL
  326. *
  327. * @method HRESULT | CCal | GetProperty |
  328. *
  329. * Read a property from a calibration structure.
  330. *
  331. * The caller is permitted to pass a property that doesn't
  332. * apply to calibration, in which case <c E_NOTIMPL>
  333. * is returned, as it should be.
  334. *
  335. * @cwrap PJOYRANGECONVERT | this
  336. *
  337. * @parm REFGUID | rguid |
  338. *
  339. * The property being retrieved.
  340. *
  341. * @parm IN REFGUID | rguid |
  342. *
  343. * The identity of the property to be obtained.
  344. *
  345. * @parm IN LPDIPROPHEADER | pdiph |
  346. *
  347. * Points to the <t DIPROPHEADER> portion of a structure
  348. * which depends on the property.
  349. *
  350. * @returns
  351. *
  352. * <c S_OK> if the operation completed successfully.
  353. *
  354. * <c E_NOTIMPL> nothing happened. The caller will do
  355. * the default thing in response to <c E_NOTIMPL>.
  356. *
  357. *****************************************************************************/
  358. STDMETHODIMP
  359. CCal_GetProperty(PJOYRANGECONVERT this, REFGUID rguid, LPDIPROPHEADER pdiph)
  360. {
  361. HRESULT hres;
  362. LPDIPROPRANGE pdiprg = CONTAINING_RECORD(pdiph, DIPROPRANGE, diph);
  363. LPDIPROPDWORD pdipdw = CONTAINING_RECORD(pdiph, DIPROPDWORD, diph);
  364. LPDIPROPCAL pdipcal = CONTAINING_RECORD(pdiph, DIPROPCAL , diph);
  365. LPDIPROPCPOINTS pdipcps = CONTAINING_RECORD(pdiph, DIPROPCPOINTS , diph);
  366. EnterProc(CCal::GetProperty, (_ "pxp", this, rguid, pdiph));
  367. switch ((DWORD)(UINT_PTR)rguid) {
  368. case (DWORD)(UINT_PTR)DIPROP_RANGE:
  369. pdiprg->lMin = this->lMin;
  370. pdiprg->lMax = this->lMax;
  371. hres = S_OK;
  372. break;
  373. case (DWORD)(UINT_PTR)DIPROP_DEADZONE:
  374. pdipdw->dwData = this->dwDz;
  375. hres = S_OK;
  376. break;
  377. case (DWORD)(UINT_PTR)DIPROP_SATURATION:
  378. pdipdw->dwData = this->dwSat;
  379. hres = S_OK;
  380. break;
  381. case (DWORD)(UINT_PTR)DIPROP_CALIBRATIONMODE:
  382. pdipdw->dwData = this->fRaw;
  383. hres = S_OK;
  384. break;
  385. case (DWORD)(UINT_PTR)DIPROP_CALIBRATION:
  386. pdipcal->lMin = this->dwPmin;
  387. pdipcal->lMax = this->dwPmax;
  388. pdipcal->lCenter = this->dwPc;
  389. hres = S_OK;
  390. break;
  391. case (DWORD)(UINT_PTR)DIPROP_CPOINTS:
  392. pdipcps->dwCPointsNum = this->dwCPointsNum;
  393. memcpy( &pdipcps->cp, &this->cp, sizeof(this->cp) );
  394. hres = S_OK;
  395. break;
  396. default:
  397. hres = E_NOTIMPL;
  398. break;
  399. }
  400. ExitOleProc();
  401. return hres;
  402. }
  403. /*****************************************************************************
  404. *
  405. * @doc INTERNAL
  406. *
  407. * @method HRESULT | CCal | SetCalibration |
  408. *
  409. * The app (hopefully a control panel) is changing the
  410. * calibration.
  411. *
  412. * @cwrap PJOYRANGECONVERT | this
  413. *
  414. * @parm IN LPCDIPROPINFO | ppropi |
  415. *
  416. * Information describing the property being set.
  417. *
  418. * @parm IN LPCDIPROPHEADER | pdiph |
  419. *
  420. * Points to the <t DIPROPHEADER> portion of a structure
  421. * which depends on the property.
  422. *
  423. * @parm HKEY | hkType |
  424. *
  425. * Registry key to use calibration information.
  426. *
  427. * @returns
  428. *
  429. * <c S_OK> if the operation completed successfully.
  430. *
  431. * <c E_NOTIMPL> nothing happened. The caller will do
  432. * the default thing in response to <c E_NOTIMPL>.
  433. *
  434. *****************************************************************************/
  435. STDMETHODIMP
  436. CCal_SetCalibration(PJOYRANGECONVERT this, LPCDIPROPINFO ppropi,
  437. LPCDIPROPHEADER pdiph, HKEY hkType)
  438. {
  439. HRESULT hres;
  440. #ifdef WINNT
  441. if( ppropi->dwDevType == DIDFT_POV ) {
  442. if( this->fPolledPOV ) {
  443. LPCDIPROPCALPOV pdipcalpov = CONTAINING_RECORD(pdiph, DIPROPCALPOV, diph);
  444. if (hkType) {
  445. LPDIPOVCALIBRATION ppov;
  446. HKEY hk;
  447. /*
  448. * We pun a DIPROPCALPOV as a DIPOVCALIBRATION.
  449. */
  450. #define CheckField(f) \
  451. CAssertF(FIELD_OFFSET(DIPROPCALPOV, l##f) - cbX(DIPROPHEADER) == \
  452. FIELD_OFFSET(DIPOVCALIBRATION, l##f))
  453. CheckField(Min);
  454. CheckField(Max);
  455. #undef CheckField
  456. ppov = pvAddPvCb(pdipcalpov, cbX(DIPROPHEADER));
  457. AssertF( !memcmp(ppov->lMin, pdipcalpov->lMin, cbX(DIPOVCALIBRATION)) );
  458. AssertF( !memcmp(ppov->lMax, pdipcalpov->lMax, cbX(DIPOVCALIBRATION)) );
  459. hres = CType_OpenIdSubkey(hkType, ppropi->dwDevType,
  460. DI_KEY_ALL_ACCESS, &hk);
  461. if (SUCCEEDED(hres)) {
  462. /*
  463. * All 0x0's for calibration is our cue to reset
  464. * to default values.
  465. */
  466. if( ppov->lMin[0] == ppov->lMin[1] == ppov->lMin[2] == ppov->lMin[3] == ppov->lMin[4] ==
  467. ppov->lMax[0] == ppov->lMax[1] == ppov->lMax[2] == ppov->lMax[3] == ppov->lMax[4] == 0 )
  468. {
  469. RegDeleteValue(hk, TEXT("Calibration")) ;
  470. } else
  471. {
  472. hres = JoyReg_SetValue(hk, TEXT("Calibration"),
  473. REG_BINARY, ppov,
  474. cbX(DIPOVCALIBRATION));
  475. }
  476. RegCloseKey(hk);
  477. }
  478. } else {
  479. hres = S_FALSE;
  480. }
  481. if (SUCCEEDED(hres)) {
  482. memcpy( this->lMinPOV, pdipcalpov->lMin, cbX(pdipcalpov->lMin) );
  483. memcpy( this->lMaxPOV, pdipcalpov->lMax, cbX(pdipcalpov->lMax) );
  484. }
  485. } else {
  486. hres = E_NOTIMPL;
  487. }
  488. } else
  489. #endif
  490. {
  491. LPCDIPROPCAL pdipcal = CONTAINING_RECORD(pdiph, DIPROPCAL, diph);
  492. if (hkType) {
  493. LPDIOBJECTCALIBRATION pcal;
  494. HKEY hk;
  495. /*
  496. * We pun a DIPROPCAL as a DIOBJECTCALIBRATION.
  497. */
  498. #define CheckField(f) \
  499. CAssertF(FIELD_OFFSET(DIPROPCAL, l##f) - cbX(DIPROPHEADER) == \
  500. FIELD_OFFSET(DIOBJECTCALIBRATION, l##f))
  501. CheckField(Min);
  502. CheckField(Max);
  503. CheckField(Center);
  504. #undef CheckField
  505. pcal = pvAddPvCb(pdipcal, cbX(DIPROPHEADER));
  506. AssertF(pcal->lMin == pdipcal->lMin);
  507. AssertF(pcal->lMax == pdipcal->lMax);
  508. AssertF(pcal->lCenter == pdipcal->lCenter);
  509. hres = CType_OpenIdSubkey(hkType, ppropi->dwDevType,
  510. DI_KEY_ALL_ACCESS, &hk);
  511. if (SUCCEEDED(hres)) {
  512. /*
  513. * All 0x0's for calibration is our cue to reset
  514. * to default values.
  515. */
  516. if( pcal->lMin == pcal->lMax &&
  517. pcal->lCenter == pcal->lMax &&
  518. pcal->lMax == 0x0 )
  519. {
  520. RegDeleteValue(hk, TEXT("Calibration")) ;
  521. } else
  522. {
  523. hres = JoyReg_SetValue(hk, TEXT("Calibration"),
  524. REG_BINARY, pcal,
  525. cbX(DIOBJECTCALIBRATION));
  526. }
  527. RegCloseKey(hk);
  528. }
  529. } else {
  530. hres = S_FALSE;
  531. }
  532. if (SUCCEEDED(hres)) {
  533. this->dwPmin = pdipcal->lMin;
  534. this->dwPmax = pdipcal->lMax;
  535. this->dwPc = pdipcal->lCenter;
  536. CCal_RecalcRange(this);
  537. }
  538. }
  539. return hres;
  540. }
  541. /*****************************************************************************
  542. *
  543. * @doc INTERNAL
  544. *
  545. * @method HRESULT | CCal | SetProperty |
  546. *
  547. * Write a property to a calibration structure.
  548. *
  549. * The caller is permitted to pass a property that doesn't
  550. * apply to calibration, in which case <c E_NOTIMPL>
  551. * is returned, as it should be.
  552. *
  553. * @cwrap PJOYRANGECONVERT | this
  554. *
  555. * @parm IN LPCDIPROPINFO | ppropi |
  556. *
  557. * Information describing the property being set.
  558. *
  559. * @parm IN LPDIPROPHEADER | pdiph |
  560. *
  561. * Points to the <t DIPROPHEADER> portion of a structure
  562. * which depends on the property.
  563. *
  564. * @parm HKEY | hkType |
  565. *
  566. * Registry key to use if setting calibration information.
  567. *
  568. * @returns
  569. *
  570. * <c S_OK> if the operation completed successfully.
  571. *
  572. * <c E_NOTIMPL> nothing happened. The caller will do
  573. * the default thing in response to <c E_NOTIMPL>.
  574. *
  575. *****************************************************************************/
  576. STDMETHODIMP
  577. CCal_SetProperty(PJOYRANGECONVERT this, LPCDIPROPINFO ppropi,
  578. LPCDIPROPHEADER pdiph, HKEY hkType)
  579. {
  580. HRESULT hres;
  581. LPCDIPROPRANGE pdiprg = (PCV)pdiph;
  582. LPCDIPROPDWORD pdipdw = (PCV)pdiph;
  583. LPCDIPROPCPOINTS pdipcps = (PCV)pdiph;
  584. LPDWORD pdw;
  585. EnterProc(CCal::SetProperty, (_ "pxp", this, ppropi->pguid, pdiph));
  586. switch ((DWORD)(UINT_PTR)ppropi->pguid) {
  587. case (DWORD)(UINT_PTR)DIPROP_RANGE:
  588. if (pdiprg->lMin <= pdiprg->lMax) {
  589. this->lMin = pdiprg->lMin;
  590. this->lMax = pdiprg->lMax;
  591. this->lC = CCal_Midpoint(this->lMin, this->lMax);
  592. CCal_RecalcRange(this);
  593. SquirtSqflPtszV(sqflCal,
  594. TEXT("CCal_SetProperty:DIPROP_RANGE: lMin: %08x, lMax: %08x"),
  595. this->lMin, this->lMax );
  596. hres = S_OK;
  597. } else {
  598. RPF("ERROR DIPROP_RANGE: lMin must be <= lMax");
  599. hres = E_INVALIDARG;
  600. }
  601. break;
  602. case (DWORD)(UINT_PTR)DIPROP_DEADZONE:
  603. pdw = &this->dwDz;
  604. goto finishfraction;
  605. case (DWORD)(UINT_PTR)DIPROP_SATURATION:
  606. pdw = &this->dwSat;
  607. goto finishfraction;
  608. finishfraction:;
  609. if (pdipdw->dwData <= RANGEDIVISIONS) {
  610. *pdw = pdipdw->dwData;
  611. CCal_RecalcRange(this);
  612. hres = S_OK;
  613. } else {
  614. RPF("SetProperty: Value must be 0 .. 10000");
  615. hres = E_INVALIDARG;
  616. }
  617. break;
  618. case (DWORD)(UINT_PTR)DIPROP_CALIBRATIONMODE:
  619. if ((pdipdw->dwData & ~DIPROPCALIBRATIONMODE_VALID) == 0) {
  620. this->fRaw = pdipdw->dwData;
  621. hres = S_OK;
  622. } else {
  623. RPF("ERROR SetProperty: invalid calibration flags");
  624. hres = E_INVALIDARG;
  625. }
  626. break;
  627. case (DWORD)(UINT_PTR)DIPROP_CALIBRATION:
  628. case (DWORD)(UINT_PTR)DIPROP_SPECIFICCALIBRATION:
  629. hres = CCal_SetCalibration(this, ppropi, pdiph, hkType);
  630. break;
  631. case (DWORD)(UINT_PTR)DIPROP_CPOINTS:
  632. this->dwCPointsNum = pdipcps->dwCPointsNum;
  633. memcpy( &this->cp, &pdipcps->cp, sizeof(this->cp) );
  634. hres = S_OK;
  635. break;
  636. default:
  637. hres = E_NOTIMPL;
  638. break;
  639. }
  640. ExitOleProc();
  641. return hres;
  642. }