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.

2238 lines
63 KiB

  1. /******************************************************************************
  2. Copyright (C) Microsoft Corporation 1985-1991. All rights reserved.
  3. Title: device.c - Multimedia Systems Media Control Interface
  4. driver for AVI.
  5. *****************************************************************************/
  6. #include "graphic.h"
  7. #include "avitask.h"
  8. #define ALIGNULONG(i) ((i+3)&(~3)) /* ULONG aligned ! */
  9. #define WIDTHBYTES(i) ((unsigned)((i+31)&(~31))/8) /* ULONG aligned ! */
  10. #define DIBWIDTHBYTES(bi) (DWORD)WIDTHBYTES((int)(bi).biWidth * (int)(bi).biBitCount)
  11. #ifdef WIN32
  12. /***************************************************************************
  13. *
  14. * @doc INTERNAL MCIAVI
  15. *
  16. * @api void | TaskWaitComplete | wait for a task to complete
  17. *
  18. ***************************************************************************/
  19. void TaskWaitComplete(NPMCIGRAPHIC npMCI)
  20. {
  21. LONG lCount;
  22. /*
  23. ** Release the critical section so that the task can complete!
  24. */
  25. lCount = npMCI->lCritRefCount;
  26. npMCI->lCritRefCount = 0;
  27. LeaveCriticalSection(&npMCI->CritSec);
  28. /*
  29. ** Use the handle given to us when we created the task to wait
  30. ** for the thread to complete
  31. */
  32. WaitForSingleObject(npMCI->hThreadTermination, INFINITE);
  33. CloseHandle(npMCI->hThreadTermination);
  34. /*
  35. ** Restore our critical section state
  36. */
  37. EnterCriticalSection(&npMCI->CritSec);
  38. npMCI->lCritRefCount = lCount;
  39. }
  40. #endif
  41. /***************************************************************************
  42. *
  43. * @doc INTERNAL MCIAVI
  44. *
  45. * @api void | TaskWait | wait for a task state
  46. * background task.
  47. *
  48. ***************************************************************************/
  49. DWORD mciaviTaskWait(NPMCIGRAPHIC npMCI, int state, BOOL fMciWait)
  50. {
  51. #ifdef WIN32
  52. long lCount;
  53. MSG msg;
  54. #endif
  55. DWORD dwStartWaitTime = timeGetTime();
  56. // #define TIMEOUT_VALUE 10000L
  57. #ifndef WIN32
  58. Assert(npMCI->hTask != GetCurrentTask());
  59. #endif
  60. /*
  61. ** either wait for a state (state > 0) or wait for not state (state < 0)
  62. **
  63. ** !!! if we want to timeout this is the place to do it.
  64. **
  65. ** !!! Should we put up a wait cursor here?
  66. */
  67. while (state < 0
  68. ? (int)npMCI->wTaskState == -state
  69. : (int)npMCI->wTaskState != state)
  70. {
  71. if (!IsTask(npMCI->hTask)) {
  72. npMCI->dwTaskError = MCIERR_DEVICE_NOT_READY;
  73. return MCIERR_DEVICE_NOT_READY;
  74. }
  75. #ifdef WIN32
  76. /*
  77. * Critical sections:
  78. *
  79. * We hold a critical section around the whole of the
  80. * WinProc (among other things). The owning thread can re-enter
  81. * a critical section, but needs to Leave the same number of times.
  82. *
  83. * When yielding here, we need to release the critical section. To avoid
  84. * the problem of multiple entries, EnterCrit is a macro that
  85. * increments an entry count (protected by the critical section), and
  86. * only goes one level into the critsec. Correspondingly,
  87. * LeaveCrit decrements the count and only actually leaves if
  88. * the count reaches 0.
  89. *
  90. * Here, however, we need to actually Enter and Leave regardless
  91. * of the count, so that we release the critical section
  92. * to other threads during the yield. Thus we don't use the macro,
  93. * and we also save/restore the critsec count so someone else
  94. * getting the critsec while we are yielding will behave correctly.
  95. */
  96. lCount = npMCI->lCritRefCount;
  97. npMCI->lCritRefCount = 0;
  98. LeaveCriticalSection(&npMCI->CritSec);
  99. /*
  100. * Sleep for > 0 because this thread may be at a higher priority than
  101. * then thread actually playing the AVI because of the use of
  102. * SetThreadPriorityBackground and Sleep(0) only relinquishes
  103. * the remainder of the time slice if another thread of the SAME
  104. * PRIORITY is waiting to run.
  105. */
  106. Sleep(10);
  107. #else
  108. // DirectedYield(npMCI->hTask);
  109. Yield();
  110. #endif
  111. #ifdef WM_AVISWP
  112. if (TRUE)
  113. #else
  114. if (fMciWait)
  115. #endif
  116. {
  117. #ifdef WIN32
  118. /*
  119. * if it's safe to yield, it's safe to poll
  120. * messages fully. This way, we will be absolutely
  121. * sure of getting the async size messages etc
  122. */
  123. //aviTaskYield();
  124. // it clearly is not safe at this point, since this
  125. // yield can cause mci to close the driver, leaving us
  126. // with nowhere to return to.
  127. // handling messages for our own window is safe and should have the
  128. // desired effect.
  129. #ifdef WM_AVISWP
  130. if (npMCI->hwnd) {
  131. if (PeekMessage(&msg, npMCI->hwnd, WM_AVISWP, WM_AVISWP, PM_REMOVE))
  132. DispatchMessage(&msg);
  133. }
  134. #endif
  135. #endif
  136. if (fMciWait && mciDriverYield(npMCI->wDevID)) {
  137. #ifdef WIN32
  138. EnterCriticalSection(&npMCI->CritSec);
  139. npMCI->lCritRefCount = lCount;
  140. #endif
  141. break;
  142. }
  143. } else {
  144. #ifdef TIMEOUT_VALUE
  145. if (timeGetTime() > dwStartWaitTime + TIMEOUT_VALUE) {
  146. Assert(0);
  147. npMCI->dwTaskError = MCIERR_DEVICE_NOT_READY;
  148. #ifdef WIN32
  149. EnterCriticalSection(&npMCI->CritSec);
  150. npMCI->lCritRefCount = lCount;
  151. #endif
  152. return MCIERR_DEVICE_NOT_READY;
  153. }
  154. #endif
  155. }
  156. #ifdef WIN32
  157. EnterCriticalSection(&npMCI->CritSec);
  158. npMCI->lCritRefCount = lCount;
  159. #endif
  160. }
  161. return 0L;
  162. }
  163. /***************************************************************************
  164. *
  165. * @doc INTERNAL MCIAVI
  166. *
  167. * @api DWORD | mciaviTaskMessage | this function sends a message to the
  168. * background task.
  169. *
  170. ***************************************************************************/
  171. DWORD mciaviTaskMessage(NPMCIGRAPHIC npMCI, int msg)
  172. {
  173. if (!IsTask(npMCI->hTask)) {
  174. npMCI->dwTaskError = MCIERR_DEVICE_NOT_READY;
  175. return npMCI->dwTaskError;
  176. }
  177. if (GetCurrentTask() == npMCI->hTask) {
  178. mciaviMessage(npMCI, msg);
  179. return npMCI->dwTaskError;
  180. }
  181. if (npMCI->wTaskState == TASKPAUSED) {
  182. DPF(("Ack! message while PAUSED!\n"));
  183. return 1; // !!! Real error?
  184. }
  185. #ifdef DEBUG
  186. if (npMCI->wTaskState != TASKIDLE) {
  187. DPF0(("Unknown task state (mciaviTaskMessage) %d\n", npMCI->wTaskState));
  188. }
  189. Assert(npMCI->wTaskState == TASKIDLE);
  190. #endif
  191. if (mciaviTaskWait(npMCI, TASKIDLE, FALSE) != 0) {
  192. DPF(("Error waiting for TASKIDLE in mciaviTaskMessage\n"));
  193. return npMCI->dwTaskError;
  194. }
  195. npMCI->dwTaskError = 0L;
  196. npMCI->wTaskState = msg;
  197. mmTaskSignal(npMCI->hTask);
  198. /*
  199. ** wait for the message to kick in
  200. */
  201. mciaviTaskWait(npMCI, -msg, FALSE);
  202. return npMCI->dwTaskError;
  203. }
  204. DWORD NEAR PASCAL StopTemporarily(NPMCIGRAPHIC npMCI, TEMPORARYSTATE FAR * ps)
  205. {
  206. DWORD dw;
  207. HWND hCallback;
  208. DPF2(("StopTemporarily: stopping from state %u.\n", npMCI->wTaskState));
  209. Assert(ps);
  210. ps->wOldTaskState = npMCI->wTaskState;
  211. ps->dwFlags = npMCI->dwFlags;
  212. ps->lTo = npMCI->lTo - (LONG)npMCI->dwBufferedVideo;
  213. ps->lFrom = npMCI->lFrom;
  214. //
  215. // setting MCIAVI_UPDATING will make sure we dont yield or do
  216. // other wierd things unless we need to. it is a bad name for the
  217. // flag I know I am sorry.
  218. //
  219. // it means we are stoping temporarily and will be restarted
  220. // the code will not do things like give our wave device
  221. // away or become the active window.
  222. //
  223. npMCI->dwFlags |= MCIAVI_UPDATING;
  224. /* Hide the delayed notification, if any, so it doesn't happen now. */
  225. hCallback = npMCI->hCallback;
  226. npMCI->hCallback = NULL;
  227. dw = DeviceStop(npMCI, MCI_WAIT);
  228. /* Restore the notification */
  229. npMCI->hCallback = hCallback;
  230. if (dw != 0 ) {
  231. if (ps->dwFlags & MCIAVI_UPDATING)
  232. npMCI->dwFlags |= MCIAVI_UPDATING;
  233. else
  234. npMCI->dwFlags &= ~MCIAVI_UPDATING;
  235. }
  236. DPF2(("StopTemporarily: stopped.\n"));
  237. return dw;
  238. }
  239. DWORD NEAR PASCAL RestartAgain(NPMCIGRAPHIC npMCI, TEMPORARYSTATE FAR * ps)
  240. {
  241. DWORD dw = 0;
  242. DWORD dwFlags = 0;
  243. DPF2(("Restart Again: restarting.\n"));
  244. Assert(ps);
  245. if (ps->dwFlags & MCIAVI_REVERSE)
  246. dwFlags = MCI_DGV_PLAY_REVERSE;
  247. // !!! Make sure that this will actually cause a repeat in all cases....
  248. if (ps->dwFlags & MCIAVI_REPEATING)
  249. npMCI->dwFlags |= MCIAVI_REPEATING;
  250. else
  251. npMCI->dwFlags &= ~MCIAVI_REPEATING;
  252. if (ps->wOldTaskState == TASKPLAYING) {
  253. /* The only flags that matter at this point are the
  254. ** VGA flags and the wait flag. If we managed to
  255. ** get a new command, neither is in effect, so it's
  256. ** okay to pass zero for these flags.
  257. */
  258. npMCI->lFrom = npMCI->lCurrentFrame;
  259. dw = DevicePlay(npMCI, ps->lTo, dwFlags | MCI_TO);
  260. } else if (ps->wOldTaskState == TASKCUEING) {
  261. /* Continue whatever we were doing */
  262. npMCI->lFrom = ps->lFrom;
  263. dw = DevicePlay(npMCI, ps->lTo, dwFlags | MCI_TO);
  264. } else if (ps->wOldTaskState == TASKPAUSED) {
  265. dw = DeviceCue(npMCI, 0, MCI_WAIT);
  266. npMCI->lTo = ps->lTo;
  267. } else if (ps->wOldTaskState == TASKIDLE) {
  268. } else {
  269. DPF(("Trying to restart: task state %u...\n", ps->wOldTaskState));
  270. Assert(0);
  271. }
  272. //
  273. // restore this flag so we can yield again.
  274. //
  275. if (ps->dwFlags & MCIAVI_UPDATING)
  276. npMCI->dwFlags |= MCIAVI_UPDATING;
  277. else
  278. npMCI->dwFlags &= ~MCIAVI_UPDATING;
  279. DPF2(("Restart Again: restarted.\n"));
  280. return dw;
  281. }
  282. /***************************************************************************
  283. *
  284. * @doc INTERNAL MCIAVI
  285. *
  286. * @api void | ShowStage | This utility function brings the default stage
  287. * window to the foreground on play, seek, step and pause commands. It
  288. * does nothing if the stage window is not the default window
  289. *
  290. * @parm NPMCIGRAPHIC | npMCI | near ptr to the instance data
  291. *
  292. ***************************************************************************/
  293. void NEAR PASCAL ShowStage(NPMCIGRAPHIC npMCI)
  294. {
  295. if (!(npMCI->dwFlags & MCIAVI_NEEDTOSHOW)) {
  296. DPF0(("ShowStage returning NEEDTOSHOW is OFF\n"));
  297. return;
  298. }
  299. if ((npMCI->dwFlags & MCIAVI_SHOWVIDEO) &&
  300. npMCI->hwnd == npMCI->hwndDefault &&
  301. ////////////!(GetWindowLong(npMCI->hwnd, GWL_STYLE) & WS_CHILD) &&
  302. (!IsWindowVisible (npMCI->hwnd) ||
  303. npMCI->hwnd != GetActiveWindow ())) {
  304. #ifdef WM_AVISWP
  305. // Get the UI thread to do the window positioning
  306. // This routine can be called on the background task while the main
  307. // routine is waiting in mciaviTaskWait
  308. SendMessage(npMCI->hwnd, WM_AVISWP, 0,
  309. SWP_NOMOVE | SWP_NOSIZE |
  310. SWP_SHOWWINDOW |
  311. (IsWindowVisible(npMCI->hwnd) ? SWP_NOACTIVATE : 0));
  312. #else
  313. SetWindowPos(npMCI->hwnd, HWND_TOP, 0, 0, 0, 0,
  314. SWP_NOMOVE | SWP_NOSIZE |
  315. SWP_SHOWWINDOW |
  316. (IsWindowVisible(npMCI->hwnd) ? SWP_NOACTIVATE : 0));
  317. #endif
  318. }
  319. //
  320. // if the movie has palette changes we need to make it the active
  321. // window otherwise the palette animation will not work right
  322. //
  323. if ((npMCI->dwFlags & MCIAVI_ANIMATEPALETTE) &&
  324. !(npMCI->dwFlags & MCIAVI_SEEKING) &&
  325. !(npMCI->dwFlags & MCIAVI_FULLSCREEN) &&
  326. !(npMCI->dwFlags & MCIAVI_UPDATING) &&
  327. npMCI->hwnd == npMCI->hwndDefault &&
  328. !(GetWindowLong(npMCI->hwnd, GWL_STYLE) & WS_CHILD)) {
  329. SetActiveWindow(npMCI->hwnd);
  330. }
  331. npMCI->dwFlags &= ~(MCIAVI_NEEDTOSHOW);
  332. }
  333. /***************************************************************************
  334. *
  335. * @doc INTERNAL MCIAVI
  336. *
  337. * @api DWORD | DeviceOpen | Open an AVI file.
  338. *
  339. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  340. *
  341. * @parm LPSTR | lpName | file name.
  342. *
  343. * @parm DWORD | dwFlags | Open flags.
  344. *
  345. * @rdesc 0 means OK, otherwise mci error
  346. *
  347. ***************************************************************************/
  348. DWORD PASCAL DeviceOpen(NPMCIGRAPHIC npMCI, DWORD dwFlags)
  349. {
  350. DWORD dwRet = 0L;
  351. npMCI->wTaskState = TASKBEINGCREATED;
  352. npMCI->uErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS |
  353. SEM_NOOPENFILEERRORBOX);
  354. #ifndef WIN32
  355. // give our PSP to the task
  356. npMCI->pspParent = GetCurrentPDB();
  357. #endif
  358. switch (mmTaskCreate(mciaviTask, &npMCI->hThreadTermination, (DWORD)(UINT)npMCI)) {
  359. case 0:
  360. // Yield to the newly created task until it has
  361. // had a chance to initialize or fail to initialize
  362. while (npMCI->wTaskState <= TASKINIT) {
  363. #ifndef WIN32
  364. Yield();
  365. #else
  366. /* we have to peekmsg here since the threads are
  367. * synchronised. but we don't need to actually
  368. * pick up any messages - so limit ourselves to the
  369. * avi window ones
  370. */
  371. Sleep(1);
  372. if (npMCI->hwnd) {
  373. MSG msg;
  374. if (PeekMessage(&msg, npMCI->hwnd, 0, 0, PM_REMOVE)) {
  375. DispatchMessage(&msg);
  376. }
  377. }
  378. #endif
  379. if (npMCI->wTaskState != TASKBEINGCREATED && !IsTask(npMCI->hTask))
  380. break;
  381. }
  382. /*
  383. * we need to do this peek message again. We may have never
  384. * entered the body of the loop above, or if this thread
  385. * gets very little cpu during the above loop, we might fail to
  386. * execute the PeekMessage above AFTER the SetWindowPos happens
  387. * in mciaviOpen. In that case, the swp resizing will not happen
  388. * until the next getmessage or peekmessage - in that case,
  389. * it could come after the ShowWindow (bad) or after
  390. * another size request (much worse).
  391. *
  392. * First check the thread opened the device successfully
  393. */
  394. if (!IsTask(npMCI->hTask)) {
  395. // Task thread failed its initialisation. Wait for the
  396. // task to terminate before returning to the user.
  397. DPF2(("Waiting for task thread to terminate\n"));
  398. #ifdef WIN32
  399. // On Win32 we must explicitly wait. On Win16, because this
  400. // "thread" does not get control back until the task thread
  401. // releases control the wait is irrelevant and is not used.
  402. TaskWaitComplete(npMCI);
  403. #endif
  404. dwRet = npMCI->dwTaskError;
  405. } else {
  406. if (npMCI->hwnd) {
  407. MSG msg;
  408. if (PeekMessage(&msg, npMCI->hwnd, 0, 0, PM_REMOVE)) {
  409. DispatchMessage(&msg);
  410. }
  411. }
  412. }
  413. break;
  414. case TASKERR_NOTASKSUPPORT:
  415. case TASKERR_OUTOFMEMORY:
  416. default:
  417. npMCI->hTask = 0;
  418. dwRet = MCIERR_OUT_OF_MEMORY;
  419. break;
  420. }
  421. SetErrorMode(npMCI->uErrorMode);
  422. return dwRet;
  423. }
  424. /***************************************************************************
  425. *
  426. * @doc INTERNAL MCIAVI
  427. *
  428. * @api DWORD | DeviceStop | Stop an AVI movie.
  429. *
  430. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  431. *
  432. * @parm DWORD | dwFlags | Flags.
  433. *
  434. * @rdesc 0 means OK, otherwise mci error
  435. *
  436. ***************************************************************************/
  437. DWORD PASCAL DeviceStop(NPMCIGRAPHIC npMCI, DWORD dwFlags)
  438. {
  439. DWORD dw = 0L;
  440. /* Stop the record or playback if the task is currently playing */
  441. if (!IsTask(npMCI->hTask))
  442. return MCIERR_DEVICE_NOT_READY;
  443. if (npMCI->wTaskState == TASKPLAYING || npMCI->wTaskState == TASKPAUSED
  444. || npMCI->wTaskState == TASKCUEING
  445. || npMCI->wTaskState == TASKSTARTING) {
  446. /* Set STOP flag - the task watches for this flag to be set. The
  447. ** STOP flag is cleared by the task when playback has stopped.
  448. */
  449. // Assert(!(npMCI->dwFlags & MCIAVI_STOP));
  450. npMCI->dwFlags |= MCIAVI_STOP;
  451. /* Send an extra signal to the task in case it is still
  452. ** blocked. This will be true if we are paused or if play
  453. ** has just completed.
  454. */
  455. mmTaskSignal(npMCI->hTask);
  456. /* Yield until playback is finished and we've really stopped. */
  457. mciaviTaskWait(npMCI, TASKIDLE, FALSE);
  458. } else {
  459. #ifdef DEBUG
  460. if (npMCI->wTaskState != TASKIDLE) {
  461. DPF0(("Unknown task state (DeviceStop) %d\n", npMCI->wTaskState));
  462. }
  463. Assert(npMCI->wTaskState == TASKIDLE); // ??? Why ???
  464. #endif
  465. }
  466. return dw;
  467. }
  468. /***************************************************************************
  469. *
  470. * @doc INTERNAL MCIAVI
  471. *
  472. * @api DWORD | DevicePause | Pause an AVI movie.
  473. *
  474. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  475. *
  476. * @parm DWORD | dwFlags | Flags.
  477. *
  478. * @rdesc 0 means OK, otherwise mci error
  479. *
  480. ***************************************************************************/
  481. DWORD PASCAL DevicePause(NPMCIGRAPHIC npMCI, DWORD dwFlags)
  482. {
  483. DWORD dw = 0L;
  484. // If we're currently seeking, allow that to finish before
  485. // pausing. This could potentially lock up the machine for
  486. // a while, but the alternatives are ugly.
  487. mciaviTaskWait(npMCI, -TASKCUEING, FALSE);
  488. // Pause the record or playback if the task is currently playing
  489. // or recording (BUSY)
  490. if (npMCI->wTaskState == TASKPAUSED) {
  491. /* We're already paused at the right place, so
  492. ** that means we did it. Reset the flag, though, just in
  493. ** case we were about to restart.
  494. */
  495. npMCI->dwFlags |= MCIAVI_PAUSE;
  496. if (dwFlags & MCI_NOTIFY)
  497. GraphicDelayedNotify(npMCI, MCI_NOTIFY_SUCCESSFUL);
  498. } else if (npMCI->wTaskState == TASKPLAYING) {
  499. npMCI->dwFlags |= MCIAVI_PAUSE | MCIAVI_WAITING;
  500. /* If the notify flag is set, set a flag which will tell us to
  501. ** send a notification when we actually pause.
  502. */
  503. if (dwFlags & MCI_NOTIFY)
  504. npMCI->dwFlags |= MCIAVI_CUEING;
  505. if (dwFlags & MCI_WAIT) {
  506. /* We have to wait to actually pause. */
  507. mciaviTaskWait(npMCI, -TASKPLAYING, TRUE);
  508. }
  509. npMCI->dwFlags &= ~(MCIAVI_WAITING);
  510. } else if (npMCI->wTaskState == TASKIDLE) {
  511. /* We're stopped. Put us in paused mode by cueing. */
  512. npMCI->lTo = npMCI->lCurrentFrame;
  513. DeviceCue(npMCI, 0, dwFlags);
  514. } else {
  515. dw = MCIERR_NONAPPLICABLE_FUNCTION;
  516. }
  517. return dw;
  518. }
  519. /***************************************************************************
  520. *
  521. * @doc INTERNAL MCIAVI
  522. *
  523. * @api DWORD | DeviceClose | Close an AVI file.
  524. *
  525. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  526. *
  527. * @rdesc 0 means OK, otherwise mci error
  528. *
  529. ***************************************************************************/
  530. DWORD PASCAL DeviceClose (NPMCIGRAPHIC npMCI)
  531. {
  532. DWORD dw = 0L;
  533. if (npMCI && IsTask(npMCI->hTask)) {
  534. /* Be sure to stop playing, one way or another... */
  535. DeviceStop(npMCI, MCI_WAIT);
  536. // task state is now TASKIDLE and blocked
  537. #ifdef DEBUG
  538. if (npMCI->wTaskState != TASKIDLE) {
  539. DPF0(("Unknown task state (DeviceClose) %d\n", npMCI->wTaskState));
  540. }
  541. Assert(npMCI->wTaskState == TASKIDLE);
  542. #endif
  543. // Set task state to TASKCLOSE - this informs the task that it is
  544. // time to die.
  545. mciaviTaskMessage(npMCI, TASKCLOSE);
  546. mciaviTaskWait(npMCI, TASKCLOSED, FALSE);
  547. #ifdef WIN32
  548. /*
  549. ** Wait for the thread to complete so the DLL doesn't get unloaded
  550. ** while it's still executing code in that thread
  551. */
  552. TaskWaitComplete(npMCI);
  553. #endif // WIN32
  554. }
  555. return dw;
  556. }
  557. /***************************************************************************
  558. *
  559. * @doc INTERNAL MCIAVI
  560. *
  561. * @api DWORD | DevicePlay | Play an AVI movie.
  562. *
  563. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  564. *
  565. * @parm DWORD | dwFlags | MCI flags from command.
  566. *
  567. * @rdesc 0 means OK, otherwise mci error
  568. *
  569. ***************************************************************************/
  570. DWORD PASCAL DevicePlay (NPMCIGRAPHIC npMCI, LONG lPlayTo, DWORD dwFlags)
  571. {
  572. HWND hCallback;
  573. DWORD dw = 0L;
  574. if (!IsTask(npMCI->hTask))
  575. return MCIERR_DEVICE_NOT_READY;
  576. /* if not already playing, start the task up. */
  577. if (dwFlags & MCI_NOTIFY) {
  578. /* Hide the delayed notification, if any, so it doesn't happen now. */
  579. hCallback = npMCI->hCallback;
  580. npMCI->hCallback = NULL;
  581. }
  582. /* First, figure out what mode to use. */
  583. if (dwFlags & (MCI_MCIAVI_PLAY_FULLSCREEN | MCI_MCIAVI_PLAY_FULLBY2)) {
  584. if (npMCI->rcDest.right - npMCI->rcDest.left >
  585. npMCI->rcMovie.right - npMCI->rcMovie.left)
  586. dwFlags |= MCI_MCIAVI_PLAY_FULLBY2;
  587. if ((dwFlags & MCI_MCIAVI_PLAY_FULLBY2) &&
  588. (npMCI->rcMovie.right <= 160) &&
  589. (npMCI->rcMovie.bottom <= 120)) {
  590. npMCI->dwFlags |= MCIAVI_ZOOMBY2;
  591. } else {
  592. npMCI->dwFlags &= ~(MCIAVI_ZOOMBY2);
  593. }
  594. // !!! This check is dumb: Some DISPDIB's may support > 320x240....
  595. if ((npMCI->rcMovie.right > 320) || (npMCI->rcMovie.bottom > 240)) {
  596. dw = MCIERR_AVI_TOOBIGFORVGA;
  597. goto Exit;
  598. }
  599. /* If playing, we have to stop so that we'll get put into DispDib
  600. mode correctly. */
  601. dw = DeviceStop(npMCI, MCI_WAIT);
  602. if (dw)
  603. goto Exit;
  604. if ((dwFlags & MCI_WAIT) && !(npMCI->dwFlags & MCIAVI_REPEATING))
  605. npMCI->dwFlags |= MCIAVI_NOBREAK;
  606. else
  607. npMCI->dwFlags &= ~(MCIAVI_NOBREAK);
  608. npMCI->dwFlags |= MCIAVI_FULLSCREEN;
  609. } else {
  610. npMCI->dwFlags &= ~(MCIAVI_FULLSCREEN);
  611. }
  612. if ((npMCI->dwFlags & MCIAVI_SEEKING) && (npMCI->lTo != npMCI->lFrom)) {
  613. /* We're currently seeking, so we have to restart to get audio
  614. ** to work.
  615. */
  616. DeviceStop(npMCI, MCI_WAIT);
  617. }
  618. /* If we're currently seeking, stop so play can begin immediately. */
  619. if (npMCI->wTaskState == TASKCUEING) {
  620. DeviceStop(npMCI, MCI_WAIT);
  621. }
  622. if (npMCI->wTaskState == TASKPLAYING || npMCI->wTaskState == TASKPAUSED) {
  623. if (((npMCI->dwFlags & MCIAVI_REVERSE) != 0) !=
  624. ((dwFlags & MCI_DGV_PLAY_REVERSE) != 0))
  625. DeviceStop(npMCI, MCI_WAIT);
  626. }
  627. // Make sure flags are cleared if they should be
  628. npMCI->dwFlags &= ~(MCIAVI_PAUSE | MCIAVI_CUEING | MCIAVI_REVERSE);
  629. if (dwFlags & MCI_DGV_PLAY_REPEAT) {
  630. npMCI->dwFlags |= MCIAVI_REPEATING;
  631. }
  632. if (dwFlags & MCI_NOTIFY) {
  633. /* Restore the notification */
  634. npMCI->hCallback = hCallback;
  635. }
  636. if (lPlayTo > npMCI->lFrames)
  637. lPlayTo = npMCI->lFrames;
  638. if (lPlayTo < 0)
  639. lPlayTo = 0;
  640. if (dwFlags & MCI_TO)
  641. npMCI->lTo = lPlayTo;
  642. if (dwFlags & MCI_DGV_PLAY_REVERSE)
  643. npMCI->dwFlags |= MCIAVI_REVERSE;
  644. npMCI->dwFlags |= MCIAVI_WAITING;
  645. if (npMCI->dwFlags & MCIAVI_NEEDTOSHOW) {
  646. ShowStage(npMCI);
  647. //
  648. // leave this set so the play code knows this is a "real" play
  649. // coming from the user, not a interal play/stop
  650. //
  651. // if the window needs shown we want to do it here if we can
  652. // not in the background task.
  653. //
  654. npMCI->dwFlags |= MCIAVI_NEEDTOSHOW;
  655. }
  656. if (npMCI->wTaskState == TASKPAUSED) {
  657. /* Wake the task up from pausing */
  658. mmTaskSignal(npMCI->hTask);
  659. } else if (npMCI->wTaskState == TASKCUEING ||
  660. npMCI->wTaskState == TASKPLAYING) {
  661. } else {
  662. /* Tell the task what to do when it wakes up */
  663. mciaviTaskMessage(npMCI, TASKSTARTING);
  664. dw = npMCI->dwTaskError;
  665. }
  666. if (dwFlags & MCI_WAIT) {
  667. // yield to playback task until playback completes but don't
  668. // yield to application - apps must use driveryield to get
  669. // out of waits.
  670. mciaviTaskWait(npMCI, TASKIDLE, TRUE);
  671. dw = npMCI->dwTaskError;
  672. }
  673. if (dwFlags & (MCI_MCIAVI_PLAY_FULLSCREEN | MCI_MCIAVI_PLAY_FULLBY2)) {
  674. MSG msg;
  675. /* Remove stray mouse and keyboard events after DispDib. */
  676. while (PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST,
  677. PM_NOYIELD | PM_REMOVE) ||
  678. PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST,
  679. PM_NOYIELD | PM_REMOVE))
  680. ;
  681. }
  682. npMCI->dwFlags &= ~(MCIAVI_WAITING);
  683. Exit:
  684. return dw;
  685. }
  686. /***************************************************************************
  687. *
  688. * @doc INTERNAL MCIAVI
  689. *
  690. * @api DWORD | DeviceResume | Play an AVI movie.
  691. *
  692. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  693. *
  694. * @parm DWORD | dwFlags | MCI flags from command.
  695. *
  696. * @rdesc 0 means OK, otherwise mci error
  697. *
  698. ***************************************************************************/
  699. DWORD PASCAL DeviceResume(NPMCIGRAPHIC npMCI, DWORD dwFlags)
  700. {
  701. DWORD dw = 0L;
  702. if (!IsTask(npMCI->hTask))
  703. return MCIERR_DEVICE_NOT_READY;
  704. dw = DevicePlay(npMCI, 0, dwFlags |
  705. ((npMCI->dwFlags & MCIAVI_REVERSE) ? MCI_DGV_PLAY_REVERSE : 0));
  706. return dw;
  707. }
  708. /***************************************************************************
  709. *
  710. * @doc INTERNAL MCIAVI
  711. *
  712. * @api DWORD | DeviceCue | Cue an AVI movie for playing.
  713. *
  714. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  715. *
  716. * @parm LONG | lTo | Frame to seek to, if MCI_TO set in <p dwFlags>.
  717. *
  718. * @parm DWORD | dwFlags | MCI flags from command.
  719. *
  720. * @rdesc 0 means OK, otherwise mci error
  721. *
  722. ***************************************************************************/
  723. DWORD PASCAL DeviceCue(NPMCIGRAPHIC npMCI, LONG lTo, DWORD dwFlags)
  724. {
  725. DWORD dw = 0L;
  726. HWND hCallback;
  727. /* if not already playing, start animation and set timer */
  728. if (!IsTask(npMCI->hTask))
  729. return MCIERR_DEVICE_NOT_READY;
  730. if (dwFlags & MCI_NOTIFY) {
  731. /* Hide the delayed notification, if any, so it doesn't happen now. */
  732. hCallback = npMCI->hCallback;
  733. npMCI->hCallback = NULL;
  734. }
  735. if (npMCI->dwFlags & MCIAVI_SEEKING) {
  736. /* We're currently seeking, so we have to start again to get audio
  737. ** to work.
  738. */
  739. DeviceStop(npMCI, MCI_WAIT);
  740. }
  741. if (dwFlags & MCI_TO) {
  742. DeviceStop(npMCI, MCI_WAIT);
  743. npMCI->lFrom = lTo;
  744. } else if (npMCI->wTaskState == TASKIDLE) {
  745. npMCI->lFrom = npMCI->lCurrentFrame;
  746. }
  747. if (dwFlags & MCI_NOTIFY) {
  748. /* Restore the notification */
  749. npMCI->hCallback = hCallback;
  750. }
  751. /* If we're ever resumed, we want to go to the end of the file. */
  752. npMCI->lTo = npMCI->lFrames;
  753. if (npMCI->wTaskState == TASKPAUSED) {
  754. /* We're already paused at the right place, so
  755. ** that means we did it.
  756. */
  757. if (dwFlags & MCI_NOTIFY)
  758. GraphicDelayedNotify(npMCI, MCI_NOTIFY_SUCCESSFUL);
  759. } else if (npMCI->wTaskState == TASKIDLE) {
  760. // !!! Is this the only condition we can do this in?
  761. npMCI->dwFlags |= MCIAVI_PAUSE | MCIAVI_CUEING | MCIAVI_WAITING;
  762. mciaviTaskMessage(npMCI, TASKSTARTING);
  763. if (dwFlags & MCI_WAIT) {
  764. mciaviTaskWait(npMCI, -TASKCUEING, TRUE);
  765. }
  766. npMCI->dwFlags &= ~(MCIAVI_WAITING);
  767. dw = npMCI->dwTaskError;
  768. } else if (npMCI->wTaskState == TASKCUEING) {
  769. npMCI->dwFlags |= MCIAVI_PAUSE | MCIAVI_CUEING | MCIAVI_WAITING;
  770. if (dwFlags & MCI_WAIT) {
  771. mciaviTaskWait(npMCI, -TASKCUEING, TRUE);
  772. }
  773. npMCI->dwFlags &= ~(MCIAVI_WAITING);
  774. dw = npMCI->dwTaskError;
  775. } else if (npMCI->wTaskState == TASKPLAYING) {
  776. npMCI->dwFlags |= MCIAVI_PAUSE | MCIAVI_CUEING | MCIAVI_WAITING;
  777. if (dwFlags & MCI_WAIT) {
  778. mciaviTaskWait(npMCI, -TASKPLAYING, TRUE);
  779. }
  780. npMCI->dwFlags &= ~(MCIAVI_WAITING);
  781. dw = npMCI->dwTaskError;
  782. } else {
  783. dw = MCIERR_NONAPPLICABLE_FUNCTION;
  784. }
  785. return dw;
  786. }
  787. /***************************************************************************
  788. *
  789. * @doc INTERNAL MCIAVI
  790. *
  791. * @api DWORD | DeviceSeek | Seek to a position in an AVI movie.
  792. *
  793. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  794. *
  795. * @parm LONG | lTo | Frame to seek to.
  796. *
  797. * @parm DWORD | dwFlags | MCI flags from command.
  798. *
  799. * @rdesc 0 means OK, otherwise mci error
  800. *
  801. ***************************************************************************/
  802. DWORD PASCAL DeviceSeek(NPMCIGRAPHIC npMCI, LONG lTo, DWORD dwFlags)
  803. {
  804. DWORD dw = 0;
  805. HWND hCallback;
  806. DPF3(("DeviceSeek\n"));
  807. /* The window will be shown by the play code. */
  808. /* If we can just shorten a previous seek, do it. */
  809. if ((npMCI->wTaskState == TASKCUEING) &&
  810. (npMCI->dwFlags & MCIAVI_SEEKING) &&
  811. (npMCI->lCurrentFrame <= lTo) &&
  812. (npMCI->lTo >= lTo)) {
  813. if (lTo != npMCI->lTo) {
  814. DPF3(("Seeking to %ld instead.\n", lTo));
  815. }
  816. npMCI->lTo = lTo;
  817. return 0L;
  818. }
  819. if (dwFlags & MCI_NOTIFY) {
  820. /* Hide the delayed notification, if any, so it doesn't happen now. */
  821. hCallback = npMCI->hCallback;
  822. npMCI->hCallback = NULL;
  823. }
  824. /* If playing, stop, so we can seek. */
  825. dw = DeviceStop(npMCI, MCI_WAIT);
  826. if (dwFlags & MCI_NOTIFY) {
  827. /* Restore the notification */
  828. npMCI->hCallback = hCallback;
  829. }
  830. // task state is now TASKIDLE and blocked
  831. if (npMCI->lCurrentFrame != lTo) {
  832. npMCI->dwFlags |= MCIAVI_WAITING;
  833. /* Essentially, we are telling the task: play just frame <lTo>.
  834. ** When it gets there, it will update the screen for us.
  835. */
  836. npMCI->lFrom = npMCI->lTo = lTo;
  837. mciaviTaskMessage(npMCI, TASKSTARTING);
  838. if (dwFlags & MCI_WAIT) {
  839. mciaviTaskWait(npMCI, -TASKCUEING, TRUE);
  840. }
  841. npMCI->dwFlags &= ~(MCIAVI_WAITING);
  842. } else {
  843. /* Be sure the window gets shown and the notify gets sent,
  844. ** even though we don't have to do anything.
  845. */
  846. if (npMCI->dwFlags & MCIAVI_NEEDTOSHOW)
  847. ShowStage(npMCI);
  848. if (dwFlags & MCI_NOTIFY)
  849. GraphicDelayedNotify(npMCI, MCI_NOTIFY_SUCCESSFUL);
  850. }
  851. return dw;
  852. }
  853. /***************************************************************************
  854. *
  855. * @doc INTERNAL MCIAVI
  856. *
  857. * @api DWORD | CheckIfActive | check to see if we are the active movie
  858. *
  859. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data block.
  860. *
  861. * @rdesc 0 means OK, otherwise mci error
  862. *
  863. ***************************************************************************/
  864. static void CheckIfActive(NPMCIGRAPHIC npMCI)
  865. {
  866. BOOL fActive;
  867. HWND hwndA;
  868. //
  869. // are we the foreground window?
  870. //
  871. // ??? should the value of <npMCI->fForceBackground> matter?
  872. //
  873. // IMPORTANT: This does NOT work under NT. The best that can
  874. // be done is to check GetForegroundWindow
  875. hwndA = GetActiveWindow();
  876. fActive = (hwndA == npMCI->hwnd) ||
  877. (GetFocus() == npMCI->hwnd) ||
  878. (IsChild(hwndA, npMCI->hwnd) && !npMCI->fForceBackground);
  879. DeviceSetActive(npMCI, fActive);
  880. }
  881. /***************************************************************************
  882. *
  883. * @doc INTERNAL MCIAVI
  884. *
  885. * @api DWORD | DeviceRealize | Updates the frame into the given DC
  886. *
  887. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data block.
  888. *
  889. * @parm BOOL | fForceBackground | Realize as background palette?
  890. *
  891. * @rdesc 0 means OK, otherwise mci error
  892. *
  893. ***************************************************************************/
  894. DWORD PASCAL DeviceRealize(NPMCIGRAPHIC npMCI)
  895. {
  896. BOOL fGetDC;
  897. BOOL fPalChanged;
  898. BOOL fAlreadyDoneThat;
  899. if (npMCI->dwFlags & MCIAVI_WANTMOVE)
  900. CheckWindowMove(npMCI, TRUE);
  901. if (fAlreadyDoneThat = (BOOL)(npMCI->dwFlags & MCIAVI_UPDATING)) {
  902. DPF(("Re-entering DeviceRealize - but we don't care"));
  903. }
  904. if (fGetDC = (npMCI->hdc == NULL)) {
  905. npMCI->hdc = GetDC(npMCI->hwnd);
  906. }
  907. npMCI->dwFlags |= MCIAVI_UPDATING;
  908. fPalChanged = PrepareDC(npMCI) > 0;
  909. if (!fAlreadyDoneThat)
  910. npMCI->dwFlags &= ~MCIAVI_UPDATING;
  911. if (fGetDC) {
  912. UnprepareDC(npMCI);
  913. ReleaseDC(npMCI->hwnd, npMCI->hdc);
  914. npMCI->hdc = NULL;
  915. }
  916. if (fPalChanged)
  917. InvalidateRect(npMCI->hwnd, &npMCI->rcDest, TRUE);
  918. CheckIfActive(npMCI);
  919. return 0L;
  920. }
  921. /***************************************************************************
  922. *
  923. * @doc INTERNAL MCIAVI
  924. *
  925. * @api DWORD | DeviceActivate | is the movie active?
  926. *
  927. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data block.
  928. *
  929. * @rdesc 0 means OK, otherwise mci error
  930. *
  931. ***************************************************************************/
  932. DWORD PASCAL DeviceSetActive(NPMCIGRAPHIC npMCI, BOOL fActive)
  933. {
  934. if (fActive)
  935. #ifdef WIN32
  936. // We must explicitly request a unicode string. %s will not
  937. // work as dprintf uses wvsprintfA
  938. DPF(("**** '%hs' is active.\n", (LPTSTR)npMCI->szFilename));
  939. #else
  940. DPF(("**** '%s' is active.\n", (LPTSTR)npMCI->szFilename));
  941. #endif
  942. //
  943. // if we are now the foreground "window" try to get the wave
  944. // device back (iff it was stolen from us)
  945. //
  946. if (fActive && (npMCI->dwFlags & MCIAVI_LOSTAUDIO)) {
  947. if (StealWaveDevice(npMCI)) {
  948. Assert(npMCI->dwFlags & MCIAVI_PLAYAUDIO);
  949. Assert(npMCI->hWave == NULL);
  950. npMCI->dwFlags &= ~MCIAVI_PLAYAUDIO;
  951. DeviceMute(npMCI, FALSE);
  952. }
  953. }
  954. return 0;
  955. }
  956. /***************************************************************************
  957. *
  958. * IsScreenDC() - returns true if the passed DC is a DC to the screen.
  959. * NOTE this checks for a DCOrg != 0, bitmaps always have
  960. * a origin of (0,0) This will give the wrong info a
  961. * fullscreen DC.
  962. *
  963. ***************************************************************************/
  964. #ifndef WIN32
  965. #define IsScreenDC(hdc) (GetDCOrg(hdc) != 0L)
  966. #else
  967. INLINE BOOL IsScreenDC(HDC hdc)
  968. {
  969. POINT pt;
  970. GetDCOrgEx(hdc, &pt);
  971. return pt.x != 0 && pt.y != 0;
  972. }
  973. #endif
  974. /***************************************************************************
  975. *
  976. * @doc INTERNAL MCIAVI
  977. *
  978. * @api DWORD | DeviceUpdate | Updates the frame into the given DC
  979. *
  980. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data block.
  981. *
  982. * @parm HDC | hDC | DC to draw frame into.
  983. *
  984. * @parm LPRECT | lprc | Update rect.
  985. *
  986. * @rdesc 0 means OK, otherwise mci error
  987. *
  988. ***************************************************************************/
  989. DWORD PASCAL DeviceUpdate(NPMCIGRAPHIC npMCI, DWORD dwFlags, HDC hdc, LPRECT lprc)
  990. {
  991. DWORD dwErr = 0L;
  992. BOOL f;
  993. HDC hdcSave;
  994. TEMPORARYSTATE ts;
  995. HWND hCallback;
  996. HCURSOR hcurPrev;
  997. RECT rc;
  998. LONG lFrameDrawn;
  999. if (!IsTask(npMCI->hTask)) {
  1000. DPF0(("Returning DEVICE_NOT_READY from DeviceUpdate\n"));
  1001. return MCIERR_DEVICE_NOT_READY;
  1002. }
  1003. if (npMCI->dwFlags & MCIAVI_UPDATING) {
  1004. DPF(("DeviceUpdate has been reentered.\n"));
  1005. Assert(0);
  1006. return MCIERR_DEVICE_NOT_READY;
  1007. }
  1008. if (npMCI->dwFlags & MCIAVI_WANTMOVE)
  1009. CheckWindowMove(npMCI, TRUE);
  1010. //
  1011. // see if we are the active movie now.
  1012. //
  1013. CheckIfActive(npMCI);
  1014. /* Setting this flag insures that the background task doesn't
  1015. ** yield while we're trying to update.
  1016. */
  1017. npMCI->dwFlags |= MCIAVI_UPDATING;
  1018. //
  1019. // mark the proper streams dirty, this will set the proper update flags
  1020. //
  1021. if (hdc)
  1022. GetClipBox(hdc, &rc);
  1023. else
  1024. rc = npMCI->rcDest;
  1025. if (lprc)
  1026. IntersectRect(&rc, &rc, lprc);
  1027. StreamInvalidate(npMCI, &rc);
  1028. //
  1029. // if they are drawing to the screen *assume* they wanted to set
  1030. // the MCI_DGV_UPDATE_PAINT flag
  1031. //
  1032. if (IsScreenDC(hdc))
  1033. dwFlags |= MCI_DGV_UPDATE_PAINT;
  1034. //
  1035. // if we are playing/seeking... (ie have a DC)
  1036. // then realize the palette now. and set the update flag if we just need to
  1037. // paint
  1038. //
  1039. // if we are paused, fall through so we can handle the case where
  1040. // a update fails
  1041. //
  1042. // !!!mabey we should rework this code to do this even if playing?
  1043. //
  1044. if (npMCI->hdc &&
  1045. (dwFlags & MCI_DGV_UPDATE_PAINT) &&
  1046. (npMCI->wTaskState != TASKPAUSED) &&
  1047. //!!! what is this?
  1048. ((npMCI->wTaskState != TASKCUEING) ||
  1049. (npMCI->lCurrentFrame <= 1) ||
  1050. (npMCI->lCurrentFrame > npMCI->lRealStart - 30)) ) {
  1051. Assert(npMCI->wTaskState == TASKPLAYING ||
  1052. npMCI->wTaskState == TASKCUEING);
  1053. UnprepareDC(npMCI);
  1054. PrepareDC(npMCI); // re-prepare
  1055. ////////
  1056. //////// a update can fail when paused so we may have to stop/restart the task
  1057. ////////
  1058. ////////if (npMCI->wTaskState == TASKPAUSED)
  1059. //////// mmTaskSignal(npMCI->hTask);
  1060. npMCI->dwFlags &= ~MCIAVI_UPDATING;
  1061. return 0L;
  1062. }
  1063. //////////////////////////////////////////////////////////////////////
  1064. //
  1065. // when we get here one of the follow applies
  1066. //
  1067. // 1. we aren't playing/seeking/...
  1068. //
  1069. // 2. we need to draw into a memory bitmap (not the screen)
  1070. //
  1071. //////////////////////////////////////////////////////////////////////
  1072. //
  1073. // are we updating to a memory bitmap?
  1074. //
  1075. if (!(dwFlags & MCI_DGV_UPDATE_PAINT))
  1076. npMCI->dwFlags |= MCIAVI_UPDATETOMEMORY;
  1077. //
  1078. // if we are using a draw device (or are in easy mode) make sure we seek
  1079. // to the frame we want and dont use the current decompress buffer that
  1080. // may not be correct.
  1081. //
  1082. if ((npMCI->dwFlags & MCIAVI_UPDATETOMEMORY) ||
  1083. (npMCI->dwFlags & MCIAVI_STUPIDMODE)) {
  1084. DPF(("DeviceUpdate: decompress buffer may be bad, ignoring it....\n"));
  1085. npMCI->lFrameDrawn = (- (LONG) npMCI->wEarlyRecords) - 1;
  1086. }
  1087. //
  1088. // honor the passed rect
  1089. //
  1090. if (lprc) {
  1091. SaveDC(hdc);
  1092. IntersectClipRect(hdc, lprc->left, lprc->top,
  1093. lprc->right, lprc->bottom);
  1094. }
  1095. //
  1096. // Always do an Update, if the update succeeds and we are at the right
  1097. // frame keep it.
  1098. //
  1099. // if it fails or the frame is wrong re-draw
  1100. //
  1101. // we need to do this because even though lFrameDrawn is a valid
  1102. // frame the draw handler may fail a update anyway (for example
  1103. // when decompressing to screen) so lFrameDrawn can be bogus and
  1104. // we do not know it until we try it.
  1105. //
  1106. lFrameDrawn = npMCI->lFrameDrawn; // save this for compare
  1107. if (npMCI->lFrameDrawn <= npMCI->lCurrentFrame &&
  1108. npMCI->lFrameDrawn >= 0) {
  1109. DPF2(("Update: redrawing frame %ld, current = %ld.\n", npMCI->lFrameDrawn, npMCI->lCurrentFrame));
  1110. /* Save the DC, in case we're playing, but need to update
  1111. ** to a memory bitmap.
  1112. */
  1113. hdcSave = npMCI->hdc;
  1114. npMCI->hdc = hdc;
  1115. /* Realize the palette here, because it will cause strange
  1116. ** things to happen if we do it in the task.
  1117. */
  1118. if (npMCI->dwFlags & MCIAVI_NEEDDRAWBEGIN) {
  1119. DrawBegin(npMCI, NULL);
  1120. if (npMCI->lFrameDrawn < npMCI->lVideoStart) {
  1121. npMCI->hdc = hdcSave;
  1122. goto SlowUpdate;
  1123. }
  1124. }
  1125. PrepareDC(npMCI); // make sure the palette is in there
  1126. // worker thread must hold critsec round all drawing
  1127. EnterCrit(npMCI);
  1128. f = DoStreamUpdate(npMCI, FALSE);
  1129. LeaveCrit(npMCI);
  1130. UnprepareDC(npMCI); // be sure to put things back....
  1131. npMCI->hdc = hdcSave;
  1132. if (!f) {
  1133. SlowUpdate:
  1134. DPF(("DeviceUpdate failed! invalidating lFrameDrawn\n"));
  1135. npMCI->lFrameDrawn = (- (LONG) npMCI->wEarlyRecords) - 1;
  1136. }
  1137. else if (npMCI->lFrameDrawn >= npMCI->lCurrentFrame-1) {
  1138. goto Exit;
  1139. }
  1140. }
  1141. DPF(("Update: drawn = %ld, current = %ld.\n", npMCI->lFrameDrawn, npMCI->lCurrentFrame));
  1142. //
  1143. // stop everything.
  1144. //
  1145. StopTemporarily(npMCI, &ts);
  1146. Assert(npMCI->hdc == NULL);
  1147. Assert(npMCI->wTaskState == TASKIDLE);
  1148. //
  1149. // the problem this tries to fix is the following:
  1150. // sometimes we are at N+1 but frame N is on the
  1151. // screen, if we now play to N+1 a mismatch will occur
  1152. //
  1153. if (lFrameDrawn >= 0 && lFrameDrawn == npMCI->lCurrentFrame-1)
  1154. npMCI->lFrom = npMCI->lTo = lFrameDrawn;
  1155. else
  1156. npMCI->lFrom = npMCI->lTo = npMCI->lCurrentFrame;
  1157. /* Realize the palette here, because it will cause strange
  1158. ** things to happen if we do it in the task.
  1159. */
  1160. npMCI->hdc = hdc;
  1161. PrepareDC(npMCI); // make sure the palette is in there
  1162. hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
  1163. /* Hide any notification, so it won't get sent... */
  1164. hCallback = npMCI->hCallback;
  1165. npMCI->hCallback = NULL;
  1166. /* Wake the task up, and wait until it quiets down. */
  1167. mciaviTaskMessage(npMCI, TASKSTARTING);
  1168. mciaviTaskWait(npMCI, TASKIDLE, FALSE);
  1169. dwErr = npMCI->dwTaskError;
  1170. npMCI->hCallback = hCallback;
  1171. // We may have just yielded.. so only set the cursor back if we
  1172. // are still the wait cursor.
  1173. if (hcurPrev) {
  1174. hcurPrev = SetCursor(hcurPrev);
  1175. if (hcurPrev != LoadCursor(NULL, IDC_WAIT))
  1176. SetCursor(hcurPrev);
  1177. }
  1178. npMCI->hdc = NULL;
  1179. if (dwErr == 0)
  1180. dwErr = RestartAgain(npMCI,&ts);
  1181. Exit:
  1182. if (lprc) {
  1183. RestoreDC(hdc, -1);
  1184. }
  1185. npMCI->dwFlags &= ~(MCIAVI_UPDATING|MCIAVI_UPDATETOMEMORY);
  1186. if (npMCI->dwFlags & MCIAVI_NEEDUPDATE) {
  1187. DPF(("**** we did a DeviceUpdate but still dirty?\n"));
  1188. }
  1189. return dwErr;
  1190. }
  1191. /***************************************************************************
  1192. *
  1193. * @doc INTERNAL MCIAVI
  1194. *
  1195. * @api DWORD | DeviceStatus | Returns the current status
  1196. *
  1197. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data block.
  1198. *
  1199. * @rdesc Returns value for MCI's return value
  1200. *
  1201. ***************************************************************************/
  1202. UINT PASCAL DeviceMode(NPMCIGRAPHIC npMCI)
  1203. {
  1204. if (!IsTask(npMCI->hTask)) {
  1205. return MCI_MODE_NOT_READY;
  1206. }
  1207. switch (npMCI->wTaskState) {
  1208. case TASKIDLE:
  1209. return MCI_MODE_STOP;
  1210. case TASKCUEING:
  1211. return MCI_MODE_SEEK;
  1212. case TASKPLAYING:
  1213. return MCI_MODE_PLAY;
  1214. case TASKPAUSED:
  1215. return MCI_MODE_PAUSE;
  1216. case TASKBEINGCREATED:
  1217. case TASKINIT:
  1218. case TASKCLOSE:
  1219. case TASKSTARTING:
  1220. case TASKREADINDEX:
  1221. default:
  1222. DPF(("Unexpected state %d in DeviceMode()\n", npMCI->wTaskState));
  1223. return MCI_MODE_NOT_READY;
  1224. }
  1225. }
  1226. /***************************************************************************
  1227. *
  1228. * @doc INTERNAL MCIAVI
  1229. *
  1230. * @api DWORD | DevicePosition | Returns the current frame
  1231. *
  1232. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data block.
  1233. *
  1234. * @parm LPLONG | lpl | returns current frame
  1235. *
  1236. * @rdesc 0 means OK, otherwise mci error
  1237. *
  1238. ***************************************************************************/
  1239. DWORD PASCAL DevicePosition(NPMCIGRAPHIC npMCI, LPLONG lpl)
  1240. {
  1241. LONG NEAR PASCAL WhatFrameIsItTimeFor(NPMCIGRAPHIC npMCI);
  1242. LONG l;
  1243. l = npMCI->lCurrentFrame - npMCI->dwBufferedVideo;
  1244. #if 0
  1245. if (npMCI->wTaskState == TASKPLAYING &&
  1246. npMCI->wPlaybackAlg != MCIAVI_ALG_INTERLEAVED)
  1247. l = WhatFrameIsItTimeFor(npMCI);
  1248. #endif
  1249. if ((npMCI->wTaskState == TASKCUEING) &&
  1250. !(npMCI->dwFlags & MCIAVI_SEEKING) &&
  1251. l < npMCI->lRealStart)
  1252. l = npMCI->lRealStart;
  1253. if (l < 0)
  1254. l = 0;
  1255. *lpl = l;
  1256. return 0L;
  1257. }
  1258. /***************************************************************************
  1259. *
  1260. * @doc INTERNAL MCIAVI
  1261. *
  1262. * @api DWORD | DeviceSetWindow | Set window for display
  1263. *
  1264. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  1265. *
  1266. * @parm HWND | hwnd | Window to display into.
  1267. *
  1268. * @rdesc 0 means OK, otherwise mci error
  1269. *
  1270. * @comm Should this only take effect at time of next play?
  1271. *
  1272. ***************************************************************************/
  1273. DWORD PASCAL DeviceSetWindow(NPMCIGRAPHIC npMCI, HWND hwnd)
  1274. {
  1275. DWORD dw = 0L;
  1276. TEMPORARYSTATE ts;
  1277. /* Stop play before changing windows. */
  1278. dw = StopTemporarily(npMCI, &ts);
  1279. if (!dw) {
  1280. npMCI->hwnd = hwnd;
  1281. if (ts.wOldTaskState == TASKIDLE) {
  1282. #if 0
  1283. DrawBegin(npMCI);
  1284. DrawEnd(npMCI);
  1285. #else
  1286. npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN;
  1287. InvalidateRect(hwnd, &npMCI->rcDest, FALSE);
  1288. #endif
  1289. }
  1290. /* Should we update the window here? */
  1291. /* Start playing again in the new window */
  1292. dw = RestartAgain(npMCI, &ts);
  1293. }
  1294. return dw;
  1295. }
  1296. /***************************************************************************
  1297. *
  1298. * @doc INTERNAL MCIAVI
  1299. *
  1300. * @api DWORD | DeviceSpeed | Adjust the playback speed of an AVI movie.
  1301. *
  1302. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  1303. *
  1304. * @parm DWORD | dwNewSpeed | New speed, where 1000 is 'normal' speed.
  1305. *
  1306. * @rdesc 0 means OK, otherwise mci error
  1307. *
  1308. * @comm If we are currently playing, we stop the device, set our flag,
  1309. * and start playing again where we left off. If we were paused,
  1310. * we end up stopped. Is this bad?
  1311. *
  1312. ***************************************************************************/
  1313. DWORD PASCAL DeviceSetSpeed(NPMCIGRAPHIC npMCI, DWORD dwNewSpeed)
  1314. {
  1315. DWORD dw = 0L;
  1316. TEMPORARYSTATE ts;
  1317. /* If new speed is the same as the old speed, return. */
  1318. if (dwNewSpeed == npMCI->dwSpeedFactor)
  1319. return 0L;
  1320. // !!! What if we were cueing or paused?
  1321. npMCI->dwSpeedFactor = dwNewSpeed;
  1322. if (npMCI->wTaskState == TASKIDLE)
  1323. return 0L;
  1324. /* We're playing, so we have to adjust the playback rate in
  1325. ** midstream. If we don't have sound going, this is pretty
  1326. ** easy. If we do have sound, we either need to speed it up
  1327. ** or slow it down or stop and start over.
  1328. */
  1329. // This code doesn't work, since there are internal variables that
  1330. // need to be updated. Therefore, just stop and restart, even if there
  1331. // isn't any sound.
  1332. #if 0
  1333. /* Figure out how fast we're playing.... */
  1334. npMCI->dwPlayMicroSecPerFrame = muldiv32(npMCI->dwMicroSecPerFrame, 1000L,
  1335. npMCI->dwSpeedFactor);
  1336. /* If there's no sound, we're done. */
  1337. if ((npMCI->nAudioStreams == 0) ||
  1338. !(npMCI->dwFlags & MCIAVI_PLAYAUDIO))
  1339. return 0L;
  1340. if (npMCI->hWave) {
  1341. /* We could potentially try to do a waveOutSetPlaybackRate() here. */
  1342. }
  1343. #endif
  1344. dw = StopTemporarily(npMCI, &ts);
  1345. if (!dw) {
  1346. dw = RestartAgain(npMCI, &ts);
  1347. }
  1348. return dw;
  1349. }
  1350. /***************************************************************************
  1351. *
  1352. * @doc INTERNAL MCIAVI
  1353. *
  1354. * @api DWORD | DeviceMute | Turn AVI sound on/off.
  1355. *
  1356. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  1357. *
  1358. * @parm BOOL | fMute | TRUE If sound should be turned off, FALSE
  1359. * if sound should stay on.
  1360. *
  1361. * @rdesc 0 means OK, otherwise mci error
  1362. *
  1363. * @comm If we are currently playing, we stop the device, set our flag,
  1364. * and start playing again where we left off. If we were paused,
  1365. * we end up stopped. Is this bad?
  1366. *
  1367. ***************************************************************************/
  1368. DWORD PASCAL DeviceMute(NPMCIGRAPHIC npMCI, BOOL fMute)
  1369. {
  1370. DWORD dw = 0L;
  1371. TEMPORARYSTATE ts;
  1372. /* If there's no audio, just return. Should this be an error? */
  1373. if (npMCI->nAudioStreams == 0)
  1374. return 0L;
  1375. /* If the mute state isn't changing, don't do anything. */
  1376. if (npMCI->dwFlags & MCIAVI_PLAYAUDIO) {
  1377. if (!fMute)
  1378. return 0L;
  1379. } else {
  1380. if (fMute)
  1381. return 0L;
  1382. }
  1383. /* Stop before changing mute */
  1384. dw = StopTemporarily(npMCI, &ts);
  1385. if (!dw) {
  1386. if (fMute)
  1387. npMCI->dwFlags &= ~MCIAVI_PLAYAUDIO;
  1388. else
  1389. npMCI->dwFlags |= MCIAVI_PLAYAUDIO;
  1390. dw = RestartAgain(npMCI, &ts);
  1391. }
  1392. return dw;
  1393. }
  1394. /***************************************************************************
  1395. *
  1396. * @doc INTERNAL MCIAVI
  1397. *
  1398. * @api DWORD | DeviceSetVolume | Set AVI volume.
  1399. *
  1400. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  1401. *
  1402. * @parm DWORD | dwVolume | ranges from 0 to 1000.
  1403. *
  1404. * @rdesc 0 means OK, otherwise mci error
  1405. *
  1406. * @comm If we are currently playing, we try to change the volume of the
  1407. * wave out device.
  1408. *
  1409. ***************************************************************************/
  1410. DWORD PASCAL DeviceSetVolume(NPMCIGRAPHIC npMCI, DWORD dwVolume)
  1411. {
  1412. DWORD dw = 0L;
  1413. npMCI->dwVolume = dwVolume;
  1414. npMCI->dwFlags |= MCIAVI_VOLUMESET;
  1415. /* clear flag to emulate volume */;
  1416. npMCI->fEmulatingVolume = FALSE;
  1417. /* If there's no audio, just return. Should this be an error? */
  1418. if (npMCI->nAudioStreams == 0)
  1419. return 0L;
  1420. dw = DeviceMute(npMCI, dwVolume == 0);
  1421. if (npMCI->hWave && dw == 0L) {
  1422. WORD wLeft;
  1423. WORD wRight;
  1424. if (LOWORD(dwVolume) >= 1000)
  1425. wLeft = 0xFFFF;
  1426. else
  1427. wLeft = (WORD) muldiv32(LOWORD(dwVolume), 32768L, 500L);
  1428. if (HIWORD(dwVolume) >= 1000)
  1429. wRight = 0xFFFF;
  1430. else
  1431. wRight = (WORD) muldiv32(HIWORD(dwVolume), 32768L, 500L);
  1432. // !!! Support left and right volume?
  1433. dw = waveOutMessage(npMCI->hWave, WODM_SETVOLUME,
  1434. MAKELONG(wLeft, wRight), 0);
  1435. if (dw != MMSYSERR_NOERROR && LOWORD(dwVolume) != 500) {
  1436. npMCI->fEmulatingVolume = TRUE;
  1437. BuildVolumeTable(npMCI);
  1438. }
  1439. dw = 0;
  1440. }
  1441. return dw;
  1442. }
  1443. /***************************************************************************
  1444. *
  1445. * @doc INTERNAL MCIAVI
  1446. *
  1447. * @api DWORD | DeviceGetVolume | Check the wave output device's current
  1448. * volume.
  1449. *
  1450. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  1451. *
  1452. * @rdesc 0 means OK, otherwise mci error
  1453. *
  1454. * @comm The volume is left in npMCI->dwVolume
  1455. *
  1456. * Issue: On devices with global volume control, like an SBPro, how should
  1457. * things work?
  1458. *
  1459. ***************************************************************************/
  1460. DWORD PASCAL DeviceGetVolume(NPMCIGRAPHIC npMCI)
  1461. {
  1462. DWORD dw;
  1463. DWORD dwVolume;
  1464. if (npMCI->hWave) {
  1465. // Get the current audio volume....
  1466. dw = waveOutMessage(npMCI->hWave, WODM_GETVOLUME,
  1467. (DWORD) (DWORD FAR *)&dwVolume, 0);
  1468. if (dw == 0) {
  1469. returnvolume:
  1470. npMCI->dwVolume = MAKELONG((UINT)muldiv32(LOWORD(dwVolume), 500L, 32768L),
  1471. (UINT)muldiv32(HIWORD(dwVolume), 500L, 32768L));
  1472. }
  1473. } else if (!(npMCI->dwFlags & MCIAVI_VOLUMESET)) {
  1474. // We have no device open, and the user hasn't chosen a
  1475. // volume yet.
  1476. //
  1477. // Try to find out what the current "default" volume is.
  1478. //
  1479. // I realy doubt zero is the current volume, try to work
  1480. // with broken cards like the windows sound system.
  1481. //
  1482. dw = waveOutGetVolume((UINT)WAVE_MAPPER, &dwVolume);
  1483. if (dw == 0 && dwVolume != 0)
  1484. goto returnvolume;
  1485. dw = waveOutGetVolume(0, &dwVolume);
  1486. if (dw == 0 && dwVolume != 0)
  1487. goto returnvolume;
  1488. return MCIERR_NONAPPLICABLE_FUNCTION;
  1489. }
  1490. return 0;
  1491. }
  1492. /***************************************************************************
  1493. *
  1494. * @doc INTERNAL MCIAVI
  1495. *
  1496. * @api DWORD | DeviceSetAudioStream | Choose which audio stream to use.
  1497. *
  1498. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  1499. *
  1500. * @parm WORD | wStream | ranges from 1 to the number of streams.
  1501. *
  1502. * @rdesc 0 means OK, otherwise mci error
  1503. *
  1504. ***************************************************************************/
  1505. DWORD PASCAL DeviceSetAudioStream(NPMCIGRAPHIC npMCI, UINT wAudioStream)
  1506. {
  1507. DWORD dw = 0L;
  1508. TEMPORARYSTATE ts;
  1509. int stream;
  1510. /* If there's no audio, just return. Should this be an error? */
  1511. if (npMCI->nAudioStreams == 0)
  1512. return 0;
  1513. for (stream = 0; stream < npMCI->streams; stream++) {
  1514. if (SH(stream).fccType == streamtypeAUDIO) {
  1515. --wAudioStream;
  1516. if (wAudioStream == 0)
  1517. break;
  1518. }
  1519. }
  1520. if (stream == npMCI->nAudioStream)
  1521. return 0;
  1522. Assert(stream < npMCI->streams);
  1523. /* Stop before changing mute */
  1524. dw = StopTemporarily(npMCI, &ts);
  1525. if (!dw) {
  1526. npMCI->psiAudio = SI(stream);
  1527. npMCI->nAudioStream = stream;
  1528. dw = RestartAgain(npMCI, &ts);
  1529. }
  1530. return dw;
  1531. }
  1532. /***************************************************************************
  1533. *
  1534. * @doc INTERNAL MCIAVI
  1535. *
  1536. * @api DWORD | DeviceSetVideoStream | Choose which video stream is the
  1537. * "default". Also can enable/disable a stream. this works for both
  1538. * video and "other" streams.
  1539. *
  1540. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  1541. *
  1542. * @parm WORD | wStream | ranges from 1 to the number of streams.
  1543. *
  1544. * @rdesc 0 means OK, otherwise mci error
  1545. *
  1546. ***************************************************************************/
  1547. DWORD PASCAL DeviceSetVideoStream(NPMCIGRAPHIC npMCI, UINT uStream, BOOL fOn)
  1548. {
  1549. DWORD dw = 0L;
  1550. TEMPORARYSTATE ts;
  1551. int stream;
  1552. STREAMINFO *psi;
  1553. //
  1554. // find the Nth non-audio, non-error stream
  1555. //
  1556. for (stream = 0; stream < npMCI->streams; stream++) {
  1557. psi = SI(stream);
  1558. if (psi->sh.fccType == streamtypeAUDIO)
  1559. continue;
  1560. if (psi->dwFlags & STREAM_ERROR)
  1561. continue;
  1562. if (--uStream == 0)
  1563. break;
  1564. }
  1565. if (stream == npMCI->streams)
  1566. return MCIERR_OUTOFRANGE;
  1567. /* Stop before changing */
  1568. dw = StopTemporarily(npMCI, &ts);
  1569. if (!dw) {
  1570. if (fOn)
  1571. psi->dwFlags |= STREAM_ENABLED;
  1572. else
  1573. psi->dwFlags &= ~STREAM_ENABLED;
  1574. if (fOn && psi->sh.fccType == streamtypeVIDEO) {
  1575. //!!! should we change the master timebase?
  1576. DOUT("Setting main video stream\n");
  1577. #if 0
  1578. //
  1579. // the master video stream is too special cased to change!
  1580. //
  1581. npMCI->psiVideo = psi;
  1582. npMCI->nVideoStream = stream;
  1583. #endif
  1584. }
  1585. if (!fOn && npMCI->nVideoStream == stream) {
  1586. DOUT("Turning off main video stream\n");
  1587. npMCI->dwFlags &= ~MCIAVI_SHOWVIDEO;
  1588. }
  1589. //
  1590. // now we turn MCIAVI_SHOWVIDEO off if no video/other streams
  1591. // are enabled.
  1592. //
  1593. npMCI->dwFlags &= ~MCIAVI_SHOWVIDEO; // assume off.
  1594. for (stream = 0; stream < npMCI->streams; stream++) {
  1595. psi = SI(stream);
  1596. if (psi->sh.fccType == streamtypeAUDIO)
  1597. continue;
  1598. if (psi->dwFlags & STREAM_ERROR)
  1599. continue;
  1600. if (!(psi->dwFlags & STREAM_ENABLED))
  1601. continue;
  1602. // at least one stream is enabled show "video"
  1603. npMCI->dwFlags |= MCIAVI_SHOWVIDEO;
  1604. }
  1605. if (!(npMCI->dwFlags & MCIAVI_SHOWVIDEO))
  1606. DOUT("All streams off\n");
  1607. dw = RestartAgain(npMCI, &ts);
  1608. }
  1609. return dw;
  1610. }
  1611. /***************************************************************************
  1612. *
  1613. ***************************************************************************/
  1614. static void MapRect(RECT *prc, RECT*prcIn, RECT *prcFrom, RECT *prcTo)
  1615. {
  1616. if (IsRectEmpty(prcFrom)) {
  1617. SetRectEmpty(prc);
  1618. }
  1619. else {
  1620. DPF0(("MapRect: In [%d %d %d %d]\n", *prcIn));
  1621. DPF0(("MapRect: From [%d %d %d %d]\n", *prcFrom));
  1622. DPF0(("MapRect: To [%d %d %d %d]\n", *prcTo));
  1623. prc->left = prcTo->left + MulDiv(prcIn->left - prcFrom->left, prcTo->right - prcTo->left, prcFrom->right - prcFrom->left);
  1624. prc->top = prcTo->top + MulDiv(prcIn->top - prcFrom->top, prcTo->bottom - prcTo->top, prcFrom->bottom - prcFrom->top);
  1625. prc->right = prcTo->left + MulDiv(prcIn->right - prcFrom->left, prcTo->right - prcTo->left, prcFrom->right - prcFrom->left);
  1626. prc->bottom= prcTo->top + MulDiv(prcIn->bottom- prcFrom->top, prcTo->bottom - prcTo->top, prcFrom->bottom - prcFrom->top);
  1627. DPF0(("MapRect: OUT [%d %d %d %d]\n", *prc));
  1628. }
  1629. }
  1630. /***************************************************************************
  1631. *
  1632. ***************************************************************************/
  1633. static void MapStreamRects(NPMCIGRAPHIC npMCI)
  1634. {
  1635. int i;
  1636. //
  1637. // now set the source and dest rects for each stream.
  1638. //
  1639. for (i=0; i<npMCI->streams; i++)
  1640. {
  1641. //
  1642. // make sure the stream rect is in bounds
  1643. //
  1644. DPF0(("SH(%d) rcFrame [%d %d %d %d]\n", i, SH(i).rcFrame));
  1645. DPF0(("SI(%d) rcSource [%d %d %d %d]\n", i, SI(i)->rcSource));
  1646. DPF0(("SI(%d) rcDest [%d %d %d %d]\n", i, SI(i)->rcDest));
  1647. DPF0(("np(%d) rcSource [%d %d %d %d]\n", i, npMCI->rcSource));
  1648. DPF0(("np(%d) rcDest [%d %d %d %d]\n", i, npMCI->rcDest ));
  1649. DPF0(("\n"));
  1650. IntersectRect(&SI(i)->rcSource, &SH(i).rcFrame, &npMCI->rcSource);
  1651. DPF0(("SI(%d) rcSource [%d %d %d %d]\n", i, SI(i)->rcSource));
  1652. DPF0(("\n"));
  1653. //
  1654. // now map the stream source rect onto the destination
  1655. //
  1656. MapRect(&SI(i)->rcDest, &SI(i)->rcSource, &npMCI->rcSource, &npMCI->rcDest);
  1657. DPF0(("SI(%d) rcSource [%d %d %d %d]\n", i, SI(i)->rcSource));
  1658. DPF0(("SI(%d) rcDest [%d %d %d %d]\n", i, SI(i)->rcDest));
  1659. DPF0(("np(%d) rcSource [%d %d %d %d]\n", i, npMCI->rcSource));
  1660. DPF0(("np(%d) rcDest [%d %d %d %d]\n", i, npMCI->rcDest ));
  1661. DPF0(("\n"));
  1662. //
  1663. // make the stream source RECT (rcSource) relative to the
  1664. // stream rect (rcFrame)
  1665. //
  1666. OffsetRect(&SI(i)->rcSource,-SH(i).rcFrame.left,-SH(i).rcFrame.top);
  1667. DPF0(("SI(%d) rcSource [%d %d %d %d]\n", i, SI(i)->rcSource));
  1668. DPF0(("SI(%d) rcDest [%d %d %d %d]\n", i, SI(i)->rcDest));
  1669. DPF0(("np(%d) rcSource [%d %d %d %d]\n", i, npMCI->rcSource));
  1670. DPF0(("np(%d) rcDest [%d %d %d %d]\n", i, npMCI->rcDest ));
  1671. DPF0(("\n"));
  1672. }
  1673. }
  1674. /***************************************************************************
  1675. *
  1676. * @doc INTERNAL MCIAVI
  1677. *
  1678. * @api DWORD | DevicePut | Change source or destination rectangle
  1679. *
  1680. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  1681. *
  1682. * @parm LPRECT | lprc | Pointer to new rectangle to use.
  1683. *
  1684. * @parm DWORD | dwFlags | Flags: will be either MCI_DGV_PUT_DESTINATION
  1685. * or MCI_DGV_PUT_SOURCE.
  1686. *
  1687. * @rdesc 0 means OK, otherwise mci error
  1688. *
  1689. * @comm
  1690. * If we end up using a custom stretch buffer, it would go here.
  1691. *
  1692. ***************************************************************************/
  1693. DWORD FAR PASCAL DevicePut(NPMCIGRAPHIC npMCI, LPRECT lprc, DWORD dwFlags)
  1694. {
  1695. RECT rc;
  1696. PRECT prcPut;
  1697. DWORD dw = 0;
  1698. if (dwFlags & MCI_DGV_PUT_DESTINATION) {
  1699. DPF2(("DevicePut: destination [%d, %d, %d, %d]\n", *lprc));
  1700. prcPut = &npMCI->rcDest;
  1701. } else {
  1702. DPF2(("DevicePut: source [%d, %d, %d, %d]\n", *lprc));
  1703. prcPut = &npMCI->rcSource;
  1704. //
  1705. // make sure source rectangle is in range.
  1706. //
  1707. // !!!should we return a error, or just fix the rectange???
  1708. //
  1709. rc = npMCI->rcMovie;
  1710. IntersectRect(lprc, &rc, lprc); // fix up the passed rect.
  1711. }
  1712. //
  1713. // check for a bogus rect. either a NULL or inverted rect is considered
  1714. // invalid.
  1715. //
  1716. // !!!NOTE we should handle a inverted rect (mirrored stretch)
  1717. //
  1718. if (lprc->left >= lprc->right ||
  1719. lprc->top >= lprc->bottom) {
  1720. DPF2(("DevicePut: invalid rectangle [%d, %d, %d, %d]\n", *lprc));
  1721. return MCIERR_OUTOFRANGE;
  1722. }
  1723. /* make sure the rect changed */
  1724. if (EqualRect(prcPut,lprc))
  1725. return 0L;
  1726. InvalidateRect(npMCI->hwnd, &npMCI->rcDest, TRUE);
  1727. rc = *prcPut; /* save it */
  1728. *prcPut = *lprc; /* change it */
  1729. InvalidateRect(npMCI->hwnd, &npMCI->rcDest, FALSE);
  1730. /* has both the dest and source been set? */
  1731. if (IsRectEmpty(&npMCI->rcDest) || IsRectEmpty(&npMCI->rcSource))
  1732. return 0L;
  1733. MapStreamRects(npMCI);
  1734. StreamInvalidate(npMCI, NULL); // invalidate the world
  1735. if (npMCI->wTaskState <= TASKIDLE) {
  1736. DPF2(("DevicePut: Idle, force DrawBegin on update\n"));
  1737. npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN;
  1738. }
  1739. else {
  1740. BOOL fRestart;
  1741. //
  1742. // we dont need to start/stop just begin again.
  1743. //
  1744. DPF2(("DevicePut: Calling DrawBegin()\n"));
  1745. if (!DrawBegin(npMCI, &fRestart)) {
  1746. return npMCI->dwTaskError;
  1747. }
  1748. if (!DoStreamUpdate(npMCI, FALSE)) {
  1749. DPF(("Put: Failed update, forcing restart....\n"));
  1750. npMCI->lFrameDrawn = (- (LONG) npMCI->wEarlyRecords) - 1;
  1751. fRestart = TRUE;
  1752. }
  1753. if (fRestart) {
  1754. TEMPORARYSTATE ts;
  1755. DPF2(("DevicePut: Stopping temporarily()\n"));
  1756. // !!! Set a flag here to prevent any more drawing
  1757. npMCI->fNoDrawing = TRUE;
  1758. if (StopTemporarily(npMCI, &ts) != 0)
  1759. return npMCI->dwTaskError;
  1760. // !!! We used to call InitDecompress here...
  1761. npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN;
  1762. RestartAgain(npMCI, &ts);
  1763. dw = npMCI->dwTaskError;
  1764. }
  1765. }
  1766. return dw;
  1767. }
  1768. /***************************************************************************
  1769. *
  1770. * @doc INTERNAL MCIAVI
  1771. *
  1772. * @api DWORD | DeviceSetPalette | Changes the override palette.
  1773. *
  1774. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  1775. *
  1776. * @parm HPALETTE | hpal | New palette to use.
  1777. *
  1778. * @rdesc 0 means OK, otherwise mci error
  1779. *
  1780. ***************************************************************************/
  1781. DWORD FAR PASCAL DeviceSetPalette(NPMCIGRAPHIC npMCI, HPALETTE hpal)
  1782. {
  1783. //
  1784. // You might think it is a good idea to allow the app to change the
  1785. // the palette while playing; think again. This will break
  1786. // MagicSchoolBus, and cause us to get into a infinite palette fight.
  1787. //
  1788. #if 0
  1789. DWORD dw = 0L;
  1790. TEMPORARYSTATE ts;
  1791. dw = StopTemporarily(npMCI, &ts);
  1792. // Remember this for later.
  1793. npMCI->hpal = hpal;
  1794. if (!dw) {
  1795. npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN;
  1796. dw = RestartAgain(npMCI, &ts);
  1797. }
  1798. return dw;
  1799. #else
  1800. if (npMCI->hpal != hpal) {
  1801. // Remember this for later.
  1802. npMCI->hpal = hpal;
  1803. // This won't happen until we restart the movie, so effectively, this
  1804. // request for a palette change will be ignored for now.
  1805. npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN;
  1806. InvalidateRect(npMCI->hwnd, NULL, TRUE);
  1807. }
  1808. return 0;
  1809. #endif
  1810. }
  1811. #ifndef LOADACTUALLYWORKS
  1812. /***************************************************************************
  1813. *
  1814. * @doc INTERNAL MCIAVI
  1815. *
  1816. * @api DWORD | DeviceLoad | Load a new AVI movie.
  1817. *
  1818. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  1819. *
  1820. * @parm DWORD | dwFlags | MCI flags from command.
  1821. *
  1822. * @rdesc 0 means OK, otherwise mci error
  1823. *
  1824. ***************************************************************************/
  1825. DWORD PASCAL DeviceLoad(NPMCIGRAPHIC npMCI)
  1826. {
  1827. DWORD dw = 0L;
  1828. if (!IsTask(npMCI->hTask))
  1829. return MCIERR_DEVICE_NOT_READY;
  1830. dw = DeviceStop(npMCI, MCI_WAIT);
  1831. // Kill the current file and open a new file...
  1832. mciaviTaskMessage(npMCI, TASKRELOAD);
  1833. dw = npMCI->dwTaskError;
  1834. return dw;
  1835. }
  1836. #endif