Windows NT 4.0 source code leak
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.

2872 lines
75 KiB

4 years ago
  1. /******************************************************************************
  2. Copyright (C) Microsoft Corporation 1985-1995. All rights reserved.
  3. Title: avitask.c - Background task that actually manipulates AVI files.
  4. *****************************************************************************/
  5. #include "graphic.h"
  6. STATICFN BOOL OnTask_ProcessRequest(NPMCIGRAPHIC npMCI);
  7. STATICFN void OnTask_WinProcRequests(NPMCIGRAPHIC npMCI, BOOL bPlaying);
  8. STATICFN void OnTask_StopTemporarily(NPMCIGRAPHIC npMCI);
  9. STATICFN void OnTask_RestartAgain(NPMCIGRAPHIC npMCI, BOOL bSetEvent);
  10. STATICFN DWORD InternalPlayStart(
  11. NPMCIGRAPHIC npMCI,
  12. DWORD dwFlags,
  13. long lReqTo,
  14. long lReqFrom,
  15. DWORD dwCallback
  16. );
  17. BOOL TryStreamUpdate(
  18. NPMCIGRAPHIC npMCI,
  19. DWORD dwFlags,
  20. HDC hdc,
  21. LPRECT lprc
  22. );
  23. /*
  24. * design overview under construction
  25. *
  26. * this file contains the core code for the worker thread that manages
  27. * playback on request from the user's thread. The worker thread also
  28. * creates a wndproc thread that owns the default playback window.
  29. */
  30. // set the error flag and signal completion of request
  31. void
  32. TaskReturns(NPMCIGRAPHIC npMCI, DWORD dwErr)
  33. {
  34. npMCI->dwReturn = dwErr;
  35. // clear the hEventSend manual-reset event now that we
  36. // have processed it
  37. ResetEvent(npMCI->hEventSend);
  38. #ifdef DEBUG
  39. // make the message invalid
  40. npMCI->message = 0;
  41. #endif
  42. // Wake up the thread that made the request
  43. DPF2(("...[%x] ok", npMCI->hRequestor));
  44. SetEvent(npMCI->hEventResponse);
  45. }
  46. /*
  47. * KillWinProcThread:
  48. *
  49. * If the winproc thread exists, send a message to the window to cause
  50. * the thread to destroy the window and terminate.
  51. */
  52. STATICFN void KillWinProcThread(NPMCIGRAPHIC npMCI)
  53. {
  54. // kill the winproc thread and wait for it to die
  55. if (npMCI->hThreadWinproc) {
  56. BOOL bOK = TRUE;
  57. if (npMCI->hwndDefault) {
  58. // must destroy on creating thread
  59. bOK = SendMessage(npMCI->hwndDefault, AVIM_DESTROY, 0, 0);
  60. if (!bOK) {
  61. DPF1(("failed to destroy window: %d", GetLastError()));
  62. } else {
  63. Assert(!IsWindow(npMCI->hwndDefault));
  64. }
  65. }
  66. // wait for winproc thread to destroy itself when the window goes
  67. if (bOK) {
  68. WaitForSingleObject(npMCI->hThreadWinproc, INFINITE);
  69. }
  70. CloseHandle(npMCI->hThreadWinproc);
  71. npMCI->hThreadWinproc = 0;
  72. }
  73. }
  74. /***************************************************************************
  75. *
  76. * @doc INTERNAL MCIAVI
  77. *
  78. * @api void | mciaviTask | This function is the background task which plays
  79. * AVI files. It is called as a result of the call to mmTaskCreate()
  80. * in DeviceOpen(). When this function returns, the task is destroyed.
  81. *
  82. * @parm DWORD | dwInst | instance data passed to mmCreateTask - contains
  83. * a pointer to an instance data block.
  84. *
  85. ***************************************************************************/
  86. void FAR PASCAL _LOADDS mciaviTask(DWORD dwInst)
  87. {
  88. NPMCIGRAPHIC npMCI;
  89. DWORD dwThreadId;
  90. BOOL bExit = FALSE;
  91. npMCI = (NPMCIGRAPHIC) dwInst;
  92. // Set this task's error mode to the same as the parent's.
  93. SetErrorMode(npMCI->uErrorMode);
  94. DPF1(("Bkgd Task hTask=%04X\n", GetCurrentTask()));
  95. /* We must set hTask up before changing the TaskState as the UI */
  96. /* thread can abort as soon as wTaskState is altered */
  97. /* NB: this comment is no longer true. Since the rewrite of */
  98. /* mciavi the UI thread will create the task thread and wait */
  99. /* until it is explicitly signalled. */
  100. npMCI->hTask = GetCurrentTask();
  101. npMCI->wTaskState = TASKIDLE;
  102. npMCI->dwTaskError = 0;
  103. // create a critical section to protect window-update code between
  104. // the worker and the winproc thread
  105. InitializeCriticalSection(&npMCI->HDCCritSec);
  106. SetNTFlags(npMCI, NTF_DELETEHDCCRITSEC);
  107. // create a critical section to protect window-request code between
  108. // the worker and the winproc thread
  109. InitializeCriticalSection(&npMCI->WinCritSec);
  110. SetNTFlags(npMCI, NTF_DELETEWINCRITSEC);
  111. // create an event to wait on for the winproc thread to tell us that
  112. // init is ok.
  113. npMCI->hEventWinProcOK = CreateEvent(NULL, FALSE, FALSE, NULL);
  114. // also a second event that the winproc signals when it has
  115. // requests for us
  116. npMCI->heWinProcRequest = CreateEvent(NULL, TRUE, FALSE, NULL);
  117. if (!npMCI->hEventWinProcOK || !npMCI->heWinProcRequest) {
  118. npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
  119. mciaviTaskCleanup(npMCI);
  120. // Abort this thread. Our waiter will wake up when our thread
  121. // handle is signalled.
  122. return;
  123. }
  124. // create a second background task to create the default window and
  125. // own the winproc.
  126. #if 0
  127. if (mmTaskCreate((LPTASKCALLBACK) aviWinProcTask,
  128. &npMCI->hThreadWinproc,
  129. (DWORD)(UINT)npMCI) == 0)
  130. #else
  131. if (npMCI->hThreadWinproc = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)aviWinProcTask,
  132. (LPVOID)npMCI, 0, &dwThreadId ))
  133. #endif
  134. {
  135. // wait to be sure that window can be created
  136. // hThreadWinproc will be signalled if the thread exits.
  137. if (WaitForMultipleObjects(2, &npMCI->hThreadWinproc,
  138. FALSE, INFINITE) == WAIT_OBJECT_0) {
  139. // thread aborted
  140. npMCI->dwTaskError = MCIERR_CREATEWINDOW;
  141. // dont wait for this thread in cleanup phase
  142. CloseHandle(npMCI->hThreadWinproc);
  143. npMCI->hThreadWinproc = 0;
  144. mciaviTaskCleanup(npMCI);
  145. // Abort this thread. Our waiter will wake up when our thread
  146. // handle is signalled.
  147. return;
  148. }
  149. } else {
  150. // could not create winproc thread
  151. npMCI->dwTaskError = MCIERR_CREATEWINDOW;
  152. mciaviTaskCleanup(npMCI);
  153. // Abort this thread. Our waiter will wake up when our thread
  154. // handle is signalled.
  155. return;
  156. }
  157. /* Open the file */
  158. // open has now been done on app thread -complete init here.
  159. if (!OpenFileInit(npMCI)) {
  160. DPF1(("Failed to complete open of AVI file\n"));
  161. mciaviTaskCleanup(npMCI);
  162. // Abort this thread. Our waiter will wake up when our thread
  163. // handle is signalled.
  164. return;
  165. }
  166. // signal that the open is complete
  167. TaskReturns(npMCI, 0);
  168. // now loop waiting for requests and processing them
  169. // ProcessRequest returns TRUE when it is time to exit
  170. // hEventSend is manual-reset so we can poll it during play.
  171. // it is reset in TaskReturns just before we signal the response
  172. // event.
  173. // hEventAllDone is set here for bDelayedComplete requests
  174. // (eg play+wait) when the entire request is satisfied and
  175. // the worker thread is back to idle. hEventResponse is set in
  176. // ProcessMessage when the request itself is complete - eg for play, once
  177. // play has started the event will be set.
  178. // we can't handle more than one thread potentially blocking on
  179. // hEventAllDone at one time, so while one thread has made a request
  180. // that could block on hEventAllDone, no other such request is permitted
  181. // from other threads. In other words, while one (user) thread has
  182. // requested a play+wait, other threads can request stop, but not
  183. // play + wait.
  184. while (!bExit) {
  185. DWORD dwObject;
  186. npMCI->wTaskState = TASKIDLE;
  187. #ifdef DEBUG
  188. // A complex assertion. If we have stopped temporarily, then we
  189. // do not want to go back to sleep.
  190. if ((npMCI->dwFlags & MCIAVI_UPDATING)
  191. && (WAIT_TIMEOUT
  192. == WaitForMultipleObjects(IDLEWAITFOR, &npMCI->hEventSend, FALSE, 0))
  193. ) {
  194. Assert(!"About to go to sleep when we should be restarting!");
  195. }
  196. #endif
  197. // the OLE guys are kind enough to create windows on this thread.
  198. // so we need to handle sent messages here to avoid deadlocks.
  199. // same is true of the similar loop in BePaused()
  200. do {
  201. #ifdef DEBUG
  202. if (npMCI->hWaiter) {
  203. DPF(("About to call MsgWaitForMultipleObjects while hWaiter=%x\n", npMCI->hWaiter));
  204. }
  205. #endif
  206. dwObject = MsgWaitForMultipleObjects(IDLEWAITFOR, &npMCI->hEventSend,
  207. FALSE, INFINITE, QS_SENDMESSAGE);
  208. DPF2(("Task woken up, dwObject=%x, hWaiter=%x\n", dwObject, npMCI->hWaiter));
  209. if (dwObject == WAIT_OBJECT_0 + IDLEWAITFOR) {
  210. MSG msg;
  211. // just a single peekmessage with NOREMOVE will
  212. // process the inter-thread send and not affect the queue
  213. PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
  214. }
  215. } while (dwObject == WAIT_OBJECT_0 + IDLEWAITFOR);
  216. switch (dwObject) {
  217. case WAIT_OBJECT_0:
  218. // check that the message has actually been set
  219. Assert(npMCI->message != 0);
  220. if (npMCI->bDelayedComplete) {
  221. if (npMCI->hWaiter && (npMCI->hWaiter != npMCI->hRequestor)) {
  222. TaskReturns(npMCI, MCIERR_DEVICE_NOT_READY);
  223. continue;
  224. } else {
  225. DPF2(("Setting hWaiter to %x\n", npMCI->hRequestor));
  226. npMCI->hWaiter = npMCI->hRequestor;
  227. }
  228. }
  229. DPF2(("get %d [%x]...", npMCI->message, npMCI->hRequestor));
  230. // we must reset this event here, or OnTask_PeekRequest will
  231. // think it is a new request and will process and
  232. // potentially complete it!!
  233. ResetEvent(npMCI->hEventSend);
  234. bExit = OnTask_ProcessRequest(npMCI);
  235. break;
  236. case WAIT_OBJECT_0+1:
  237. default:
  238. //
  239. // winproc request to do something to the task - while idle
  240. //
  241. #ifdef DEBUG
  242. if (dwObject != WAIT_OBJECT_0+1) {
  243. DPF2(("dwObject == %d\n", dwObject));
  244. }
  245. #endif
  246. Assert(dwObject == WAIT_OBJECT_0+1);
  247. OnTask_WinProcRequests(npMCI, FALSE);
  248. // this request may have resulted in a temporary stop - so we
  249. // need to restart
  250. if (npMCI->dwFlags & MCIAVI_UPDATING) {
  251. OnTask_RestartAgain(npMCI, FALSE);
  252. }
  253. }
  254. // if we have stopped temporarily to restart with new params,
  255. // then don't signal completion. However if we did restart
  256. // and now everything is quiescent, signal any waiter that happens
  257. // to be around. This code is common to both the winproc request
  258. // and user request legs, as it is possible to stop and restart
  259. // from both winproc and user requests.
  260. if (npMCI->hWaiter && (!(npMCI->dwFlags & MCIAVI_UPDATING))) {
  261. SetEvent(npMCI->hEventAllDone);
  262. } else {
  263. if (npMCI->hWaiter) {
  264. DPF2(("Would have Set hEventAllDone, but am updating\n"));
  265. }
  266. }
  267. // QUERY: if we had processed all the requests, and therefore the
  268. // two events on which we were waiting had been reset, AND
  269. // MCIAVI_UPDATING is set (due to a temporary stop) then perhaps
  270. // we ought to call OnTask_RestartAgain here. This would mean that
  271. // all the ugly RestartAgain calls within OnTask_ProcessRequest
  272. // could be removed.
  273. }
  274. // be sure to wake him up before cleanup just in case
  275. if (npMCI->hWaiter) {
  276. DPF2(("Setting hEventAllDone before closing\n"));
  277. SetEvent(npMCI->hEventAllDone);
  278. }
  279. // close the window and destroy the winproc thread before any other
  280. // cleanup, so that paints or realizes don't happen during
  281. // a partially closed state
  282. KillWinProcThread(npMCI);
  283. mciaviCloseFile(npMCI);
  284. mciaviTaskCleanup(npMCI);
  285. return;
  286. }
  287. /***************************************************************************
  288. *
  289. * @doc INTERNAL MCIAVI
  290. *
  291. * @api void | mciaviTaskCleanup | called when the background task
  292. * is being destroyed. This is where critical cleanup goes.
  293. *
  294. ***************************************************************************/
  295. void FAR PASCAL mciaviTaskCleanup(NPMCIGRAPHIC npMCI)
  296. {
  297. npMCI->hTask = 0;
  298. /* Closing the driver causes any currently saved notifications */
  299. /* to abort. */
  300. GraphicDelayedNotify(npMCI, MCI_NOTIFY_ABORTED);
  301. GdiFlush();
  302. // if still alive, kill the winproc thread and wait for it to die
  303. KillWinProcThread(npMCI);
  304. // free winproc <-> worker thread communication resources
  305. if (npMCI->hEventWinProcOK) {
  306. CloseHandle(npMCI->hEventWinProcOK);
  307. }
  308. if (npMCI->heWinProcRequest) {
  309. CloseHandle(npMCI->heWinProcRequest);
  310. }
  311. if (TestNTFlags(npMCI, NTF_DELETEWINCRITSEC)) {
  312. DeleteCriticalSection(&npMCI->WinCritSec);
  313. }
  314. if (TestNTFlags(npMCI, NTF_DELETEHDCCRITSEC)) {
  315. DeleteCriticalSection(&npMCI->HDCCritSec);
  316. }
  317. //
  318. // call a MSVideo shutdown routine.
  319. //
  320. }
  321. // task message functions
  322. // utility functions called on worker thread
  323. /***************************************************************************
  324. *
  325. * @doc INTERNAL MCIAVI
  326. *
  327. * @api void | ShowStage | This utility function brings the default stage
  328. * window to the foreground on play, seek, step and pause commands. It
  329. * does nothing if the stage window is not the default window
  330. *
  331. * @parm NPMCIGRAPHIC | npMCI | near ptr to the instance data
  332. *
  333. ***************************************************************************/
  334. void NEAR PASCAL ShowStage(NPMCIGRAPHIC npMCI)
  335. {
  336. if (!(npMCI->dwFlags & MCIAVI_NEEDTOSHOW))
  337. return;
  338. // don't show stage if we are working in response to a winproc
  339. // update request, as this could cause deadlock and is any case
  340. // pointless - if the window is now hidden, we can't possibly need
  341. // to paint it!
  342. if (npMCI->bDoingWinUpdate) {
  343. return;
  344. }
  345. if ((npMCI->dwFlags & MCIAVI_SHOWVIDEO) &&
  346. npMCI->hwndPlayback == npMCI->hwndDefault &&
  347. ////////////!(GetWindowLong(npMCI->hwnd, GWL_STYLE) & WS_CHILD) &&
  348. (!IsWindowVisible (npMCI->hwndPlayback) ||
  349. npMCI->hwndPlayback != GetActiveWindow ())) {
  350. // if this is our window, then we need to show it
  351. // ourselves
  352. if (npMCI->hwndDefault == npMCI->hwndPlayback) {
  353. WinCritCheckOut(npMCI);
  354. PostMessage(npMCI->hwndPlayback, AVIM_SHOWSTAGE, 0, 0);
  355. } else {
  356. SetWindowPos(npMCI->hwndPlayback, HWND_TOP, 0, 0, 0, 0,
  357. SWP_NOMOVE | SWP_NOSIZE |
  358. SWP_SHOWWINDOW |
  359. (IsWindowVisible(npMCI->hwndPlayback) ? SWP_NOACTIVATE : 0));
  360. }
  361. }
  362. //
  363. // if the movie has palette changes we need to make it the active
  364. // window otherwise the palette animation will not work right
  365. //
  366. if ((npMCI->dwFlags & MCIAVI_ANIMATEPALETTE) &&
  367. !(npMCI->dwFlags & MCIAVI_SEEKING) &&
  368. !(npMCI->dwFlags & MCIAVI_FULLSCREEN) &&
  369. !(npMCI->dwFlags & MCIAVI_UPDATING) &&
  370. npMCI->hwndPlayback == npMCI->hwndDefault &&
  371. !(GetWindowLong(npMCI->hwndPlayback, GWL_STYLE) & WS_CHILD)) {
  372. // if this is our window, then we need to show it
  373. // ourselves
  374. if (npMCI->hwndDefault == npMCI->hwndPlayback) {
  375. // force activation even if visible
  376. WinCritCheckOut(npMCI);
  377. PostMessage(npMCI->hwndPlayback, AVIM_SHOWSTAGE, TRUE, 0);
  378. } else {
  379. SetActiveWindow(npMCI->hwndPlayback);
  380. }
  381. }
  382. npMCI->dwFlags &= ~(MCIAVI_NEEDTOSHOW);
  383. }
  384. //
  385. // called to save state if we stop temporarily to change something.
  386. // we will restart with OnTask_RestartAgain. Called on worker thread from
  387. // somewhere in aviTaskCheckRequests
  388. STATICFN void OnTask_StopTemporarily(NPMCIGRAPHIC npMCI)
  389. {
  390. // save old state and flags
  391. npMCI->oldState = npMCI->wTaskState;
  392. npMCI->oldTo = npMCI->lTo;
  393. npMCI->oldFrom = npMCI->lFrom;
  394. npMCI->oldFlags = npMCI->dwFlags;
  395. npMCI->oldCallback = (DWORD) npMCI->hCallback;
  396. npMCI->dwFlags |= (MCIAVI_UPDATING | MCIAVI_STOP);
  397. DPF(("StopTemp: OldState=%d, oldTo=%d, oldFrom=%d, oldFlags=%8x\n",
  398. npMCI->oldState, npMCI->oldTo, npMCI->oldFrom, npMCI->oldFlags));
  399. }
  400. // called from worker thread on completion of a (idle-time) request
  401. // to restart a suspended play function
  402. //
  403. // responsible for signalling hEventResponse once the restart is complete
  404. // (or failed).
  405. STATICFN void OnTask_RestartAgain(NPMCIGRAPHIC npMCI, BOOL bSetEvent)
  406. {
  407. DWORD dwErr;
  408. DWORD dwFlags = 0;
  409. long lFrom;
  410. UINT wNotify;
  411. // we're restarting after a temporary stop- so clear the flag.
  412. // also turn off REPEATING - we might reset this in a moment
  413. npMCI->dwFlags &= ~(MCIAVI_UPDATING | MCIAVI_REPEATING);
  414. // Turn on the repeating flag if it was originally set
  415. npMCI->dwFlags |= (npMCI->oldFlags & MCIAVI_REPEATING);
  416. if (npMCI->oldFlags & MCIAVI_REVERSE) {
  417. dwFlags |= MCI_DGV_PLAY_REVERSE;
  418. }
  419. switch (npMCI->oldState) {
  420. case TASKPAUSED:
  421. // get to the old From frame and pause when you get there
  422. npMCI->dwFlags |= MCIAVI_PAUSE; // Make sure we end up paused
  423. // NOTE: InternalPlayStart no longer clears the PAUSE flag
  424. lFrom = npMCI->oldFrom;
  425. break;
  426. case TASKCUEING:
  427. // npMCI->dwFlags should still say whether to pause at
  428. // end of cueing or play
  429. lFrom = npMCI->oldFrom;
  430. dwFlags |= MCI_TO; // Stop when we get to the right frame
  431. break;
  432. case TASKPLAYING:
  433. lFrom = npMCI->lCurrentFrame;
  434. dwFlags |= MCI_TO;
  435. break;
  436. default:
  437. DPF(("asked to restart to idle (%d) state", npMCI->oldState));
  438. if (bSetEvent) {
  439. TaskReturns(npMCI, 0);
  440. }
  441. return;
  442. }
  443. DPF2(("RestartAgain calling InternalPlayStart, flags=%8x\n",dwFlags));
  444. dwErr = InternalPlayStart(npMCI, dwFlags,
  445. npMCI->oldTo, lFrom, npMCI->oldCallback);
  446. if (bSetEvent && dwErr) {
  447. TaskReturns(npMCI, dwErr);
  448. }
  449. if (!dwErr) {
  450. wNotify = mciaviPlayFile(npMCI, bSetEvent);
  451. // if we stopped to pick up new params without actually completing the
  452. // the play (OnTask_StopTemporarily) then MCIAVI_UPDATING will be set
  453. if (! (npMCI->dwFlags & MCIAVI_UPDATING)) {
  454. // perform any notification
  455. if (wNotify != MCI_NOTIFY_FAILURE) {
  456. GraphicDelayedNotify(npMCI, wNotify);
  457. }
  458. }
  459. }
  460. }
  461. /***************************************************************************
  462. *
  463. * IsScreenDC() - returns true if the passed DC is a DC to the screen.
  464. * NOTE this checks for a DCOrg != 0, bitmaps always have
  465. * a origin of (0,0) This will give the wrong info a
  466. * fullscreen DC.
  467. *
  468. ***************************************************************************/
  469. #ifndef _WIN32
  470. #define IsScreenDC(hdc) (GetDCOrg(hdc) != 0L)
  471. #else
  472. INLINE BOOL IsScreenDC(HDC hdc)
  473. {
  474. return (WindowFromDC(hdc) != NULL);
  475. }
  476. #endif
  477. // called from several task side functions to initiate play
  478. // when stopped. All you need to do is call mciaviPlayFile
  479. // once this function returns
  480. STATICFN DWORD
  481. InternalPlayStart(
  482. NPMCIGRAPHIC npMCI,
  483. DWORD dwFlags,
  484. long lReqTo,
  485. long lReqFrom,
  486. DWORD dwCallback
  487. )
  488. {
  489. long lTo, lFrom;
  490. DWORD dwRet;
  491. if (dwFlags & (MCI_MCIAVI_PLAY_FULLSCREEN | MCI_MCIAVI_PLAY_FULLBY2)) {
  492. // do nothing here - handled in fullproc
  493. } else {
  494. if (!IsWindow(npMCI->hwndPlayback)) {
  495. return MCIERR_NO_WINDOW;
  496. }
  497. npMCI->dwFlags |= MCIAVI_NEEDTOSHOW;
  498. }
  499. /* Range checks : 0 < 'from' <= 'to' <= last frame */
  500. if (dwFlags & MCI_TO) {
  501. lTo = lReqTo;
  502. if (lTo < 0L || lTo > npMCI->lFrames) {
  503. return MCIERR_OUTOFRANGE;
  504. }
  505. } else {
  506. if (dwFlags & MCI_DGV_PLAY_REVERSE)
  507. lTo = 0;
  508. else
  509. lTo = npMCI->lFrames;
  510. dwFlags |= MCI_TO;
  511. }
  512. // if no from setting, then get current position
  513. if (dwFlags & MCI_FROM) {
  514. lFrom = lReqFrom;
  515. if (lFrom < 0L || lFrom > npMCI->lFrames) {
  516. return MCIERR_OUTOFRANGE;
  517. }
  518. } else if (dwRet = InternalGetPosition(npMCI, &lFrom)) {
  519. return dwRet;
  520. }
  521. /* check 'to' and 'from' relationship. */
  522. if (lTo < lFrom)
  523. dwFlags |= MCI_DGV_PLAY_REVERSE;
  524. if ((lFrom < lTo) && (dwFlags & MCI_DGV_PLAY_REVERSE)) {
  525. return MCIERR_OUTOFRANGE;
  526. }
  527. /* If the test flag is set, return without doing anything. */
  528. /* Question: do we have to check for more possible errors? */
  529. if (dwFlags & MCI_TEST) {
  530. return 0;
  531. }
  532. npMCI->lFrom = lFrom;
  533. if (dwFlags & MCI_DGV_PLAY_REPEAT) {
  534. /* If from position isn't given, repeat from either the beginning or
  535. ** end of file as appropriate.
  536. */
  537. npMCI->lRepeatFrom =
  538. (dwFlags & MCI_FROM) ? lFrom :
  539. ((dwFlags & MCI_DGV_PLAY_REVERSE) ? npMCI->lFrames : 0);
  540. }
  541. /* if not already playing, start the task up. */
  542. /* First, figure out what mode to use. */
  543. if (dwFlags & (MCI_MCIAVI_PLAY_FULLSCREEN | MCI_MCIAVI_PLAY_FULLBY2)) {
  544. if (npMCI->rcDest.right - npMCI->rcDest.left >
  545. npMCI->rcMovie.right - npMCI->rcMovie.left)
  546. dwFlags |= MCI_MCIAVI_PLAY_FULLBY2;
  547. if ((dwFlags & MCI_MCIAVI_PLAY_FULLBY2) &&
  548. (npMCI->rcMovie.right <= 160) &&
  549. (npMCI->rcMovie.bottom <= 120)) {
  550. npMCI->dwFlags |= MCIAVI_ZOOMBY2;
  551. } else {
  552. npMCI->dwFlags &= ~(MCIAVI_ZOOMBY2);
  553. }
  554. if ((dwFlags & MCI_WAIT) && !(npMCI->dwFlags & MCIAVI_REPEATING))
  555. npMCI->dwFlags |= MCIAVI_NOBREAK;
  556. else
  557. npMCI->dwFlags &= ~(MCIAVI_NOBREAK);
  558. npMCI->dwFlags |= MCIAVI_FULLSCREEN;
  559. } else {
  560. npMCI->dwFlags &= ~(MCIAVI_FULLSCREEN);
  561. }
  562. // Make sure flags are cleared if they should be
  563. //npMCI->dwFlags &= ~(MCIAVI_PAUSE | MCIAVI_CUEING | MCIAVI_REVERSE);
  564. // PAUSE is NOT turned off, otherwise we cannot RestartAgain to a
  565. // paused state.
  566. npMCI->dwFlags &= ~(MCIAVI_CUEING | MCIAVI_REVERSE);
  567. if (dwFlags & MCI_DGV_PLAY_REPEAT) {
  568. npMCI->dwFlags |= MCIAVI_REPEATING;
  569. }
  570. /* Don't set up notify until here, so that the seek won't make it happen*/
  571. // idle so no current notify
  572. if (dwFlags & MCI_NOTIFY) {
  573. GraphicSaveCallback(npMCI, (HANDLE) (UINT)dwCallback);
  574. }
  575. if (lTo > npMCI->lFrames)
  576. lTo = npMCI->lFrames;
  577. if (lTo < 0)
  578. lTo = 0;
  579. if (dwFlags & MCI_TO)
  580. npMCI->lTo = lTo;
  581. DPF2(("InternalPlayStart Flags=%8x, ReqTo=%d ReqFrom=%d To=%d\n",
  582. dwFlags, lReqTo, lReqFrom, lTo));
  583. if (dwFlags & MCI_DGV_PLAY_REVERSE)
  584. npMCI->dwFlags |= MCIAVI_REVERSE;
  585. if (npMCI->dwFlags & MCIAVI_NEEDTOSHOW) {
  586. ShowStage(npMCI);
  587. //
  588. // leave this set so the play code knows this is a "real" play
  589. // coming from the user, not an internal play/stop
  590. //
  591. // if the window needs shown we want to do it here if we can
  592. // not in the background task.
  593. //
  594. npMCI->dwFlags |= MCIAVI_NEEDTOSHOW;
  595. }
  596. return 0;
  597. }
  598. // called at task idle time to initiate a play request -
  599. // the worker thread is NOT busy playing, seeking, cueing or paused
  600. // at this point.
  601. //
  602. // responsible for calling TaskReturns() appropriately.
  603. void
  604. OnTask_Play(NPMCIGRAPHIC npMCI)
  605. {
  606. DWORD dwRet;
  607. DWORD dwMCIFlags = npMCI->dwParamFlags;
  608. LPMCI_DGV_PLAY_PARMS lpPlay = (LPMCI_DGV_PLAY_PARMS)npMCI->lParam;
  609. long lTo, lFrom;
  610. UINT wNotify;
  611. if (lpPlay != NULL) {
  612. lTo = lpPlay->dwTo;
  613. lFrom = lpPlay->dwFrom;
  614. } else {
  615. dwMCIFlags &= ~(MCI_TO | MCI_FROM);
  616. }
  617. npMCI->dwFlags &= ~MCIAVI_REPEATING;
  618. // need to convert to frames before calling InternalPlayStart
  619. if (dwMCIFlags & MCI_TO) {
  620. lTo = ConvertToFrames(npMCI, lTo);
  621. }
  622. if (dwMCIFlags & MCI_FROM) {
  623. lFrom = ConvertToFrames(npMCI, lFrom);
  624. }
  625. dwRet = InternalPlayStart(npMCI, dwMCIFlags, lTo, lFrom,
  626. npMCI->dwReqCallback);
  627. if (dwRet || (dwMCIFlags & MCI_TEST)) {
  628. TaskReturns(npMCI, dwRet);
  629. return;
  630. }
  631. // actually play the file
  632. wNotify = mciaviPlayFile(npMCI, TRUE);
  633. // if we stopped to pick up new params without actually completing the
  634. // the play (OnTask_StopTemporarily) then MCIAVI_UPDATING will be set
  635. if (! (npMCI->dwFlags & MCIAVI_UPDATING)) {
  636. // perform any notification
  637. if (wNotify != MCI_NOTIFY_FAILURE) {
  638. GraphicDelayedNotify(npMCI, wNotify);
  639. }
  640. }
  641. return;
  642. }
  643. //
  644. // called to process a play request when play is actually happening.
  645. // if parameters can be adjusted without stopping the current play,
  646. // returns FALSE. Also if the request is rejected (and hEventResponse
  647. // signalled) because of some error, returns FALSE indicating no need to
  648. // stop. Otherwise returns TRUE, so that OnTask_Play() will
  649. // be called after stopping the current play.
  650. BOOL
  651. OnTask_PlayDuringPlay(NPMCIGRAPHIC npMCI)
  652. {
  653. DWORD dwFlags = npMCI->dwParamFlags;
  654. LPMCI_DGV_PLAY_PARMS lpPlay = (LPMCI_DGV_PLAY_PARMS)npMCI->lParam;
  655. long lTo, lFrom;
  656. DWORD dwRet;
  657. // since this is a real play request coming from the user we need
  658. // to show the stage window
  659. if (dwFlags & (MCI_MCIAVI_PLAY_FULLSCREEN | MCI_MCIAVI_PLAY_FULLBY2)) {
  660. // do nothing here - handled in fullproc
  661. } else {
  662. npMCI->dwFlags |= MCIAVI_NEEDTOSHOW;
  663. }
  664. // can be called with null lpPlay (in the resume case)
  665. // in this case, to and from will be left unchanged
  666. // if you pass lpPlay, then to and from will be set to defaults even
  667. // if you don't set MCI_TO and MCI_FROM
  668. if (lpPlay == NULL) {
  669. dwFlags &= ~(MCI_TO | MCI_FROM);
  670. }
  671. /* Range checks : 0 < 'from' <= 'to' <= last frame */
  672. if (dwFlags & MCI_TO) {
  673. lTo = ConvertToFrames(npMCI, lpPlay->dwTo);
  674. if (lTo < 0L || lTo > npMCI->lFrames) {
  675. TaskReturns(npMCI, MCIERR_OUTOFRANGE);
  676. return FALSE;
  677. }
  678. } else {
  679. // don't touch to and from for resume
  680. if (lpPlay) {
  681. if (dwFlags & MCI_DGV_PLAY_REVERSE)
  682. lTo = 0;
  683. else
  684. lTo = npMCI->lFrames;
  685. dwFlags |= MCI_TO;
  686. } else {
  687. lTo = npMCI->lTo;
  688. }
  689. }
  690. // if no from setting, then get current position
  691. if (dwFlags & MCI_FROM) {
  692. lFrom = ConvertToFrames(npMCI, lpPlay->dwFrom);
  693. if (lFrom < 0L || lFrom > npMCI->lFrames) {
  694. TaskReturns(npMCI, MCIERR_OUTOFRANGE);
  695. return FALSE;
  696. }
  697. } else if (dwRet = InternalGetPosition(npMCI, &lFrom)) {
  698. TaskReturns(npMCI, dwRet);
  699. return FALSE;
  700. }
  701. /* check 'to' and 'from' relationship. */
  702. if (lTo < lFrom)
  703. dwFlags |= MCI_DGV_PLAY_REVERSE;
  704. if ((lFrom < lTo) && (dwFlags & MCI_DGV_PLAY_REVERSE)) {
  705. TaskReturns(npMCI, MCIERR_OUTOFRANGE);
  706. return FALSE;
  707. }
  708. /* If the test flag is set, return without doing anything. */
  709. /* Question: do we have to check for more possible errors? */
  710. if (dwFlags & MCI_TEST) {
  711. TaskReturns(npMCI, 0L);
  712. return FALSE;
  713. }
  714. /* We want any previous playing to be aborted if and only if a 'from'
  715. ** parameter is specified. If only a new 'to' parameter is specified,
  716. ** we can just change the 'to' value, and play will stop at the
  717. ** proper time.
  718. **
  719. ** Also abort the play if we have lost the audio. An explicit play
  720. ** command has been issued and we should try and get the audio again.
  721. */
  722. // if it's new from position or we are seeking to the wrong stop,
  723. // or we are reversing the direction of play,
  724. // or we had lost the audio
  725. // then we need to stop.
  726. if ( (dwFlags & MCI_FROM)
  727. || (dwFlags & (MCI_MCIAVI_PLAY_FULLSCREEN | MCI_MCIAVI_PLAY_FULLBY2))
  728. || ((npMCI->dwFlags & MCIAVI_SEEKING) && (npMCI->lTo != lFrom))
  729. || (npMCI->wTaskState == TASKCUEING)
  730. || (npMCI->dwFlags & MCIAVI_LOSTAUDIO)
  731. || (((npMCI->dwFlags & MCIAVI_REVERSE) != 0) != ((dwFlags & MCI_DGV_PLAY_REVERSE) != 0))
  732. ) {
  733. // we can't continue the play - we have to stop, and then pick up
  734. // this request again in OnTask_Play().
  735. // this will abort the current notify
  736. return TRUE;
  737. }
  738. // ok to continue the current play with revised params.
  739. // set the from position correctly
  740. npMCI->lFrom = lFrom;
  741. /* If we're changing the "to" position, abort any pending notify. */
  742. if (lTo != npMCI->lTo) {
  743. GraphicDelayedNotify (npMCI, MCI_NOTIFY_ABORTED);
  744. }
  745. /* Don't set up notify until here, so that the seek won't make it happen*/
  746. if (dwFlags & MCI_NOTIFY) {
  747. GraphicSaveCallback(npMCI, (HANDLE) (UINT)npMCI->dwReqCallback);
  748. }
  749. // Make sure flags are cleared if they should be
  750. npMCI->dwFlags &= ~(MCIAVI_PAUSE | MCIAVI_CUEING | MCIAVI_REVERSE | MCIAVI_LOSEAUDIO);
  751. /* Set up the 'repeat' flags */
  752. npMCI->dwFlags &= ~(MCIAVI_REPEATING);
  753. if (dwFlags & MCI_DGV_PLAY_REPEAT) {
  754. /* If from position isn't given, repeat from either the beginning or
  755. ** end of file as appropriate.
  756. **
  757. ** if no lpPlay is given, then don't change repeatfrom
  758. */
  759. if (lpPlay) {
  760. npMCI->lRepeatFrom =
  761. (dwFlags & MCI_FROM) ? lFrom :
  762. ((dwFlags & MCI_DGV_PLAY_REVERSE) ? npMCI->lFrames : 0);
  763. }
  764. npMCI->dwFlags |= MCIAVI_REPEATING;
  765. }
  766. /* if not already playing, start the task up. */
  767. if (lTo > npMCI->lFrames)
  768. lTo = npMCI->lFrames;
  769. if (lTo < 0)
  770. lTo = 0;
  771. if (dwFlags & MCI_TO)
  772. npMCI->lTo = lTo;
  773. if (dwFlags & MCI_DGV_PLAY_REVERSE)
  774. npMCI->dwFlags |= MCIAVI_REVERSE;
  775. if (npMCI->dwFlags & MCIAVI_NEEDTOSHOW) {
  776. ShowStage(npMCI);
  777. //
  778. // leave this set so the play code knows this is a "real" play
  779. // coming from the user, not an internal play/stop
  780. //
  781. // if the window needs shown we want to do it here if we can
  782. // not in the background task.
  783. //
  784. npMCI->dwFlags |= MCIAVI_NEEDTOSHOW;
  785. }
  786. //everything adjusted - tell user ok and return to playing
  787. TaskReturns(npMCI, 0);
  788. return FALSE;
  789. }
  790. void OnTask_Realize(NPMCIGRAPHIC npMCI)
  791. {
  792. DWORD dw;
  793. EnterHDCCrit(npMCI);
  794. dw = InternalRealize(npMCI);
  795. LeaveHDCCrit(npMCI);
  796. TaskReturns(npMCI, dw);
  797. }
  798. DWORD InternalRealize(NPMCIGRAPHIC npMCI)
  799. {
  800. BOOL fGetDC;
  801. BOOL fPalChanged;
  802. #ifndef _WIN32
  803. BOOL fAlreadyDoneThat;
  804. #endif
  805. HDCCritCheckIn(npMCI);
  806. if (npMCI->dwFlags & MCIAVI_WANTMOVE)
  807. CheckWindowMove(npMCI, TRUE);
  808. #ifndef _WIN32
  809. if (fAlreadyDoneThat = (BOOL)(npMCI->dwFlags & MCIAVI_UPDATING)) {
  810. DPF(("Re-entering InternalRealize - but we don't care, npMCI=%8x\n",npMCI));
  811. }
  812. #endif
  813. if (!IsTask(npMCI->hTask))
  814. return(0L);
  815. if (fGetDC = (npMCI->hdc == NULL)) {
  816. npMCI->hdc = GetDC(npMCI->hwndPlayback);
  817. Assert(npMCI->hdc != NULL);
  818. }
  819. #ifndef _WIN32
  820. // this only prevents playback window alignment - which is not done
  821. // for NT anyway
  822. npMCI->dwFlags |= MCIAVI_UPDATING;
  823. #endif
  824. fPalChanged = PrepareDC(npMCI) > 0;
  825. #ifndef _WIN32
  826. if (!fAlreadyDoneThat)
  827. npMCI->dwFlags &= ~MCIAVI_UPDATING;
  828. #endif
  829. if (fGetDC) {
  830. UnprepareDC(npMCI);
  831. ReleaseDC(npMCI->hwndPlayback, npMCI->hdc);
  832. npMCI->hdc = NULL;
  833. HDCCritCheckIn(npMCI);
  834. }
  835. if (fPalChanged)
  836. InvalidateRect(npMCI->hwndPlayback, &npMCI->rcDest, TRUE);
  837. CheckIfActive(npMCI);
  838. return 0L;
  839. }
  840. void OnTask_Update(NPMCIGRAPHIC npMCI)
  841. {
  842. RECT rc;
  843. LPMCI_DGV_UPDATE_PARMS lpParms = (LPMCI_DGV_UPDATE_PARMS) npMCI->lParam;
  844. DWORD dwFlags = npMCI->dwFlags;
  845. DWORD dwErr;
  846. rc.left = lpParms->ptOffset.x;
  847. rc.top = lpParms->ptOffset.y;
  848. rc.right = lpParms->ptOffset.x + lpParms->ptExtent.x;
  849. rc.bottom = lpParms->ptOffset.y + lpParms->ptExtent.y;
  850. dwErr = Internal_Update (npMCI, dwFlags, lpParms->hDC, (dwFlags & MCI_DGV_RECT) ? &rc : NULL);
  851. //now, where were we ?
  852. if (!dwErr && (npMCI->dwFlags & MCIAVI_UPDATING)) {
  853. OnTask_RestartAgain(npMCI, TRUE);
  854. } else {
  855. TaskReturns(npMCI, dwErr);
  856. }
  857. }
  858. BOOL OnTask_UpdateDuringPlay(NPMCIGRAPHIC npMCI)
  859. {
  860. RECT userrc, rc;
  861. LPMCI_DGV_UPDATE_PARMS lpParms = (LPMCI_DGV_UPDATE_PARMS) npMCI->lParam;
  862. DWORD dwFlags = npMCI->dwFlags;
  863. HDC hdc = lpParms->hDC;
  864. userrc.left = lpParms->ptOffset.x;
  865. userrc.top = lpParms->ptOffset.y;
  866. userrc.right = lpParms->ptOffset.x + lpParms->ptExtent.x;
  867. userrc.bottom = lpParms->ptOffset.y + lpParms->ptExtent.y;
  868. //
  869. // mark the proper streams dirty, this will set the proper update flags
  870. //
  871. if (hdc)
  872. GetClipBox(hdc, &rc);
  873. else
  874. rc = npMCI->rcDest;
  875. if (dwFlags & MCI_DGV_RECT)
  876. IntersectRect(&rc, &rc, &userrc);
  877. StreamInvalidate(npMCI, &rc);
  878. //
  879. // if they are drawing to the screen *assume* they wanted to set
  880. // the MCI_DGV_UPDATE_PAINT flag
  881. //
  882. if (IsScreenDC(hdc))
  883. dwFlags |= MCI_DGV_UPDATE_PAINT;
  884. // we are playing now (we have a dc). just realize
  885. // the palette and set the update flag
  886. // unless we are painting to a memory dc.
  887. //
  888. // if we are paused, fall through so we can handle the case where
  889. // a update fails
  890. //
  891. // !!!mabey we should rework this code to do this even if playing?
  892. //
  893. if (npMCI->hdc &&
  894. (dwFlags & MCI_DGV_UPDATE_PAINT) &&
  895. (npMCI->wTaskState != TASKPAUSED) &&
  896. //!!! what is this?
  897. ((npMCI->wTaskState != TASKCUEING) ||
  898. (npMCI->lCurrentFrame <= 1) ||
  899. (npMCI->lCurrentFrame > npMCI->lRealStart - 30)) ) {
  900. Assert(npMCI->wTaskState == TASKPLAYING ||
  901. npMCI->wTaskState == TASKCUEING);
  902. EnterHDCCrit(npMCI);
  903. UnprepareDC(npMCI);
  904. PrepareDC(npMCI); // re-prepare
  905. LeaveHDCCrit(npMCI);
  906. // all ok - no need for stop.
  907. TaskReturns(npMCI, 0);
  908. return FALSE;
  909. }
  910. // try to use DoStreamUpdate - if this fails, we need to stop
  911. if (TryStreamUpdate(npMCI, dwFlags, hdc,
  912. (dwFlags & MCI_DGV_RECT) ? &userrc : NULL)) {
  913. // we are playing and so have an hdc. However, we have just
  914. // done a update to another hdc. switching back to the original
  915. // hdc without this will fail
  916. PrepareDC(npMCI);
  917. TaskReturns(npMCI, 0);
  918. return FALSE;
  919. }
  920. // otherwise we need to stop to do this
  921. // indicate that we should restart after doing this, and
  922. // save enough state to do this
  923. OnTask_StopTemporarily(npMCI);
  924. return TRUE;
  925. }
  926. // attempt repaint using DoStreamUpdate - if this fails (eg wrong frame)
  927. // then you need to use mciaviPlayFile to do it (to/from same frame)
  928. BOOL
  929. TryStreamUpdate(
  930. NPMCIGRAPHIC npMCI,
  931. DWORD dwFlags,
  932. HDC hdc,
  933. LPRECT lprc
  934. )
  935. {
  936. HDC hdcSave;
  937. BOOL f;
  938. //
  939. // are we updating to a memory bitmap?
  940. //
  941. if (!(dwFlags & MCI_DGV_UPDATE_PAINT))
  942. npMCI->dwFlags |= MCIAVI_UPDATETOMEMORY;
  943. //
  944. // if we are using a draw device (or are in stupid mode) make sure we seek
  945. // to the frame we want and dont use the current decompress buffer that
  946. // may not be correct.
  947. //
  948. if ((npMCI->dwFlags & MCIAVI_UPDATETOMEMORY) ||
  949. (npMCI->dwFlags & MCIAVI_STUPIDMODE)) {
  950. DPF(("DeviceUpdate: decompress buffer may be bad, ignoring it....\n"));
  951. npMCI->lFrameDrawn = (- (LONG) npMCI->wEarlyRecords) - 1;
  952. }
  953. //
  954. // honor the passed rect
  955. //
  956. if (lprc) {
  957. Assert(hdc);
  958. SaveDC(hdc);
  959. IntersectClipRect(hdc, lprc->left, lprc->top,
  960. lprc->right, lprc->bottom);
  961. }
  962. //
  963. // Always do an Update, if the update succeeds and we are at the right
  964. // frame keep it.
  965. //
  966. // if it fails or the frame is wrong need to re-draw using play.
  967. //
  968. // we need to do this because even though lFrameDrawn is a valid
  969. // frame the draw handler may fail a update anyway (for example
  970. // when decompressing to screen) so lFrameDrawn can be bogus and
  971. // we do not know it until we try it.
  972. //
  973. if (npMCI->lFrameDrawn <= npMCI->lCurrentFrame &&
  974. npMCI->lFrameDrawn >= 0) {
  975. DPF2(("Update: redrawing frame %ld, current = %ld.\n", npMCI->lFrameDrawn, npMCI->lCurrentFrame));
  976. /* Save the DC, in case we're playing, but need to update
  977. ** to a memory bitmap.
  978. */
  979. // worker thread must hold critsec round all drawing
  980. EnterHDCCrit(npMCI);
  981. hdcSave = npMCI->hdc;
  982. npMCI->hdc = hdc;
  983. /* Realize the palette here, because it will cause strange
  984. ** things to happen if we do it in the task.
  985. */
  986. if (npMCI->dwFlags & MCIAVI_NEEDDRAWBEGIN) {
  987. DrawBegin(npMCI, NULL);
  988. if (npMCI->lFrameDrawn < npMCI->lVideoStart) {
  989. npMCI->hdc = hdcSave;
  990. HDCCritCheckIn(npMCI);
  991. npMCI->lFrameDrawn = (- (LONG) npMCI->wEarlyRecords) - 1;
  992. LeaveHDCCrit(npMCI);
  993. return FALSE; // need to use play
  994. }
  995. }
  996. PrepareDC(npMCI); // make sure the palette is in there
  997. f = DoStreamUpdate(npMCI, FALSE);
  998. UnprepareDC(npMCI); // be sure to put things back....
  999. Assert(hdc == npMCI->hdc);
  1000. HDCCritCheckIn(npMCI);
  1001. npMCI->hdc = hdcSave;
  1002. LeaveHDCCrit(npMCI);
  1003. if (!f) {
  1004. DPF(("DeviceUpdate failed! invalidating lFrameDrawn\n"));
  1005. npMCI->lFrameDrawn = (- (LONG) npMCI->wEarlyRecords) - 1;
  1006. Assert(!lprc);
  1007. }
  1008. else if (npMCI->lFrameDrawn >= npMCI->lCurrentFrame-1) {
  1009. if (lprc) {
  1010. RestoreDC(hdc, -1);
  1011. }
  1012. npMCI->dwFlags &= ~(MCIAVI_UPDATING|MCIAVI_UPDATETOMEMORY);
  1013. if (npMCI->dwFlags & MCIAVI_NEEDUPDATE) {
  1014. DPF(("**** we did a DeviceUpdate but still dirty?\n"));
  1015. }
  1016. return TRUE;
  1017. }
  1018. //return FALSE; Drop through
  1019. }
  1020. return FALSE;
  1021. }
  1022. // called in stopped case to paint from OnTask_Update, and
  1023. // also on winproc thread (when stopped). Not called during play.
  1024. DWORD Internal_Update(NPMCIGRAPHIC npMCI, DWORD dwFlags, HDC hdc, LPRECT lprc)
  1025. {
  1026. DWORD dwErr = 0L;
  1027. HWND hCallback;
  1028. HCURSOR hcurPrev;
  1029. RECT rc;
  1030. LONG lFrameDrawn;
  1031. if (npMCI->dwFlags & MCIAVI_WANTMOVE)
  1032. CheckWindowMove(npMCI, TRUE);
  1033. //
  1034. // see if we are the active movie now.
  1035. //
  1036. CheckIfActive(npMCI);
  1037. //
  1038. // mark the proper streams dirty, this will set the proper update flags
  1039. //
  1040. if (hdc)
  1041. GetClipBox(hdc, &rc);
  1042. else
  1043. rc = npMCI->rcDest;
  1044. if (lprc)
  1045. IntersectRect(&rc, &rc, lprc);
  1046. StreamInvalidate(npMCI, &rc);
  1047. //
  1048. // if they are drawing to the screen *assume* they wanted to set
  1049. // the MCI_DGV_UPDATE_PAINT flag
  1050. //
  1051. if (IsScreenDC(hdc))
  1052. dwFlags |= MCI_DGV_UPDATE_PAINT;
  1053. lFrameDrawn = npMCI->lFrameDrawn; // save this for compare
  1054. // try to use DoStreamUpdate
  1055. if (TryStreamUpdate(npMCI, dwFlags, hdc, lprc)) {
  1056. return 0;
  1057. }
  1058. // no - need to use Play
  1059. // note we are already stopped at this point.
  1060. //
  1061. // the problem this tries to fix is the following:
  1062. // sometimes we are at N+1 but frame N is on the
  1063. // screen, if we now play to N+1 a mismatch will occur
  1064. //
  1065. if (lFrameDrawn >= 0 && lFrameDrawn == npMCI->lCurrentFrame-1)
  1066. npMCI->lFrom = npMCI->lTo = lFrameDrawn;
  1067. else
  1068. npMCI->lFrom = npMCI->lTo = npMCI->lCurrentFrame;
  1069. /* Realize the palette here, because it will cause strange
  1070. ** things to happen if we do it in the task.
  1071. */
  1072. EnterHDCCrit(npMCI);
  1073. npMCI->hdc = hdc;
  1074. PrepareDC(npMCI); // make sure the palette is in there
  1075. LeaveHDCCrit(npMCI);
  1076. hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
  1077. /* Hide any notification, so it won't get sent... */
  1078. hCallback = npMCI->hCallback;
  1079. npMCI->hCallback = NULL;
  1080. mciaviPlayFile(npMCI, FALSE);
  1081. npMCI->hCallback = hCallback;
  1082. // We may have just yielded.. so only set the cursor back if we
  1083. // are still the wait cursor.
  1084. if (hcurPrev) {
  1085. hcurPrev = SetCursor(hcurPrev);
  1086. if (hcurPrev != LoadCursor(NULL, IDC_WAIT))
  1087. SetCursor(hcurPrev);
  1088. }
  1089. //HDCCritCheckIn(npMCI) ??? This is an atomic operation - and
  1090. // why are we setting it to NULL here ??
  1091. npMCI->hdc = NULL;
  1092. if (lprc) {
  1093. RestoreDC(hdc, -1);
  1094. }
  1095. npMCI->dwFlags &= ~(MCIAVI_UPDATETOMEMORY);
  1096. if (npMCI->dwFlags & MCIAVI_NEEDUPDATE) {
  1097. DPF(("**** we did a DeviceUpdate but still dirty?\n"));
  1098. }
  1099. return dwErr;
  1100. }
  1101. void
  1102. OnTask_PauseDuringPlay(NPMCIGRAPHIC npMCI)
  1103. {
  1104. DWORD dwFlags = npMCI->dwParamFlags;
  1105. DPF3(("Pause during play\n"));
  1106. // no pause during cueing
  1107. if (npMCI->wTaskState == TASKCUEING) {
  1108. // leave event sent - wait till later
  1109. return;
  1110. }
  1111. // save the notify
  1112. if (dwFlags & MCI_NOTIFY) {
  1113. GraphicSaveCallback(npMCI, (HANDLE) (UINT)npMCI->dwReqCallback);
  1114. }
  1115. // what about delayed completion pause ?
  1116. // especially "pause" followed by "pause wait"
  1117. if (dwFlags & MCI_WAIT) {
  1118. // indicate hEventAllDone should be set on Pause, not
  1119. // on idle (ie at final stop)
  1120. npMCI->dwFlags |= MCIAVI_WAITING;
  1121. }
  1122. if (npMCI->wTaskState == TASKPAUSED) {
  1123. // all done already
  1124. if (dwFlags & MCI_NOTIFY) {
  1125. GraphicDelayedNotify(npMCI, MCI_NOTIFY_SUCCESSFUL);
  1126. }
  1127. } else if (npMCI->wTaskState == TASKPLAYING) {
  1128. // remember to pause
  1129. npMCI->dwFlags |= MCIAVI_PAUSE;
  1130. if (dwFlags & MCI_NOTIFY) {
  1131. // remember to send a notify when we pause
  1132. npMCI->dwFlags |= MCIAVI_CUEING;
  1133. }
  1134. }
  1135. TaskReturns(npMCI, 0);
  1136. }
  1137. void
  1138. OnTask_Cue(NPMCIGRAPHIC npMCI, DWORD dwFlags, long lTo)
  1139. {
  1140. UINT wNotify;
  1141. DPF3(("OnTask_Cue: dwFlags=%8x, To=%d\n", dwFlags, lTo));
  1142. GraphicDelayedNotify(npMCI, MCI_NOTIFY_ABORTED);
  1143. if (dwFlags & MCI_NOTIFY) {
  1144. GraphicSaveCallback(npMCI, (HANDLE) (UINT)npMCI->dwReqCallback);
  1145. }
  1146. /* Clear the 'repeat' flags */
  1147. npMCI->dwFlags &= ~(MCIAVI_REPEATING);
  1148. if (dwFlags & MCI_TO) {
  1149. npMCI->lFrom = lTo;
  1150. } else if (npMCI->wTaskState == TASKIDLE) {
  1151. npMCI->lFrom = npMCI->lCurrentFrame;
  1152. }
  1153. /* If we're ever resumed, we want to go to the end of the file. */
  1154. npMCI->lTo = npMCI->lFrames;
  1155. npMCI->dwFlags |= MCIAVI_PAUSE | MCIAVI_CUEING;
  1156. if (dwFlags & MCI_WAIT) {
  1157. npMCI->dwFlags |= MCIAVI_WAITING;
  1158. }
  1159. wNotify = mciaviPlayFile(npMCI, TRUE);
  1160. // if we stopped to pick up new params without actually completing the
  1161. // the play (OnTask_StopTemporarily) then MCIAVI_UPDATING will be set
  1162. if (! (npMCI->dwFlags & MCIAVI_UPDATING)) {
  1163. // perform any notification
  1164. if (wNotify != MCI_NOTIFY_FAILURE) {
  1165. GraphicDelayedNotify(npMCI, wNotify);
  1166. }
  1167. }
  1168. }
  1169. BOOL
  1170. OnTask_CueDuringPlay(NPMCIGRAPHIC npMCI)
  1171. {
  1172. DWORD dw = 0L;
  1173. DWORD dwFlags = npMCI->dwParamFlags;
  1174. long lTo = npMCI->lParam;
  1175. DPF3(("OnTask_CueDuringPlay\n"));
  1176. if (npMCI->dwFlags & MCIAVI_SEEKING) {
  1177. /* We're currently seeking, so we have to start again to get audio
  1178. ** to work.
  1179. */
  1180. return TRUE;
  1181. }
  1182. if (dwFlags & MCI_TO) {
  1183. return TRUE;
  1184. }
  1185. /* Clear the 'repeat' flags */
  1186. npMCI->dwFlags &= ~(MCIAVI_REPEATING);
  1187. GraphicDelayedNotify(npMCI, MCI_NOTIFY_ABORTED);
  1188. if (dwFlags & MCI_NOTIFY) {
  1189. GraphicSaveCallback(npMCI, (HANDLE) (UINT)npMCI->dwReqCallback);
  1190. }
  1191. /* If we're ever resumed, we want to go to the end of the file. */
  1192. npMCI->lTo = npMCI->lFrames;
  1193. if (npMCI->wTaskState == TASKPAUSED) {
  1194. /* We're already paused at the right place, so
  1195. ** that means we did it.
  1196. */
  1197. if (dwFlags & MCI_NOTIFY)
  1198. GraphicDelayedNotify(npMCI, MCI_NOTIFY_SUCCESSFUL);
  1199. // complete completed
  1200. TaskReturns(npMCI, 0);
  1201. // delayed completion is also done!
  1202. if (dwFlags & MCI_WAIT) {
  1203. SetEvent(npMCI->hEventAllDone);
  1204. }
  1205. // don't drop through to the second TaskReturns() below!
  1206. return FALSE;
  1207. } else if ((npMCI->wTaskState == TASKCUEING) ||
  1208. (npMCI->wTaskState == TASKPLAYING)) {
  1209. // ask for pause on completion of cueing/playing,
  1210. // and for notify and hEventAllDone when pause reached
  1211. npMCI->dwFlags |= MCIAVI_PAUSE | MCIAVI_CUEING;
  1212. if (dwFlags & MCI_WAIT) {
  1213. npMCI->dwFlags |= MCIAVI_WAITING;
  1214. }
  1215. } else {
  1216. TaskReturns (npMCI, MCIERR_NONAPPLICABLE_FUNCTION);
  1217. return FALSE;
  1218. }
  1219. TaskReturns(npMCI, 0);
  1220. return FALSE;
  1221. }
  1222. void OnTask_Seek(NPMCIGRAPHIC npMCI)
  1223. {
  1224. UINT wNotify;
  1225. DWORD dwFlags = npMCI->dwParamFlags;
  1226. long lTo = npMCI->lParam;
  1227. DPF3(("DeviceSeek - to frame %d (CurrentFrame==%d) Current State is %d\n", lTo, npMCI->lCurrentFrame, npMCI->wTaskState));
  1228. /* The window will be shown by the play code. */
  1229. // task state is now TASKIDLE and blocked
  1230. if (dwFlags & MCI_NOTIFY) {
  1231. GraphicSaveCallback(npMCI, (HANDLE) (UINT)npMCI->dwReqCallback);
  1232. }
  1233. /* Clear the 'repeat' flags */
  1234. npMCI->dwFlags &= ~(MCIAVI_REPEATING);
  1235. if (npMCI->lCurrentFrame != lTo) {
  1236. /* Essentially, we are telling the task: play just frame <lTo>.
  1237. ** When it gets there, it will update the screen for us.
  1238. */
  1239. npMCI->lFrom = npMCI->lTo = lTo;
  1240. wNotify = mciaviPlayFile(npMCI, TRUE);
  1241. // if we stopped to pick up new params without actually completing the
  1242. // the play (OnTask_StopTemporarily) then MCIAVI_UPDATING will be set
  1243. if (! (npMCI->dwFlags & MCIAVI_UPDATING)) {
  1244. // perform any notification
  1245. if (wNotify != MCI_NOTIFY_FAILURE) {
  1246. GraphicDelayedNotify(npMCI, wNotify);
  1247. }
  1248. }
  1249. } else {
  1250. // task complete
  1251. TaskReturns(npMCI, 0);
  1252. /* Be sure the window gets shown and the notify gets sent,
  1253. ** even though we don't have to do anything.
  1254. */
  1255. if (npMCI->dwFlags & MCIAVI_NEEDTOSHOW)
  1256. ShowStage(npMCI);
  1257. if (dwFlags & MCI_NOTIFY)
  1258. GraphicDelayedNotify(npMCI, MCI_NOTIFY_SUCCESSFUL);
  1259. }
  1260. }
  1261. OnTask_SeekDuringPlay(NPMCIGRAPHIC npMCI)
  1262. {
  1263. long lTo = npMCI->lParam;
  1264. DWORD dwFlags = npMCI->dwParamFlags;
  1265. DPF3(("DeviceSeek - to frame %d (CurrentFrame==%d) Current State is %d\n", lTo, npMCI->lCurrentFrame, npMCI->wTaskState));
  1266. /* The window will be shown by the play code. */
  1267. /* If we can just shorten a previous seek, do it. */
  1268. if ((npMCI->wTaskState == TASKCUEING) &&
  1269. (npMCI->dwFlags & MCIAVI_SEEKING) &&
  1270. (npMCI->lCurrentFrame <= lTo) &&
  1271. (npMCI->lTo >= lTo)) {
  1272. npMCI->lTo = lTo;
  1273. /* Clear the 'repeat' flags */
  1274. npMCI->dwFlags &= ~(MCIAVI_REPEATING);
  1275. GraphicDelayedNotify (npMCI, MCI_NOTIFY_ABORTED);
  1276. if (dwFlags & MCI_NOTIFY) {
  1277. GraphicSaveCallback(npMCI, (HANDLE) (UINT)npMCI->dwReqCallback);
  1278. }
  1279. TaskReturns(npMCI, 0);
  1280. return FALSE;
  1281. }
  1282. // we have to stop to do this seek
  1283. return TRUE;
  1284. }
  1285. void OnTask_SetWindow(NPMCIGRAPHIC npMCI)
  1286. {
  1287. npMCI->hwndPlayback = (HWND) npMCI->lParam;
  1288. npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN;
  1289. InvalidateRect(npMCI->hwndPlayback, &npMCI->rcDest, FALSE);
  1290. /* Should we update the window here? */
  1291. /* Start playing again in the new window (if we had to stop) */
  1292. //now, where were we ?
  1293. if (npMCI->dwFlags & MCIAVI_UPDATING) {
  1294. OnTask_RestartAgain(npMCI, TRUE);
  1295. } else {
  1296. TaskReturns(npMCI, 0);
  1297. }
  1298. }
  1299. void OnTask_SetSpeed(NPMCIGRAPHIC npMCI)
  1300. {
  1301. npMCI->dwSpeedFactor = (DWORD)npMCI->lParam;
  1302. // if we stopped to do this, then restart whatever we were doing
  1303. if (npMCI->dwFlags & MCIAVI_UPDATING) {
  1304. OnTask_RestartAgain(npMCI, TRUE);
  1305. } else {
  1306. TaskReturns(npMCI, 0);
  1307. }
  1308. }
  1309. BOOL
  1310. OnTask_SetSpeedDuringPlay(NPMCIGRAPHIC npMCI)
  1311. {
  1312. /* If new speed is the same as the old speed, done. */
  1313. if ((DWORD)npMCI->lParam == npMCI->dwSpeedFactor) {
  1314. TaskReturns(npMCI, 0);
  1315. return FALSE;
  1316. }
  1317. // otherwise we have to stop and restart
  1318. OnTask_StopTemporarily(npMCI);
  1319. return TRUE;
  1320. }
  1321. void OnTask_WaveSteal(NPMCIGRAPHIC npMCI) {
  1322. DPF2(("OnTask_WaveSteal, '%ls' hTask=%04X\n", (LPSTR)npMCI->szFilename, npMCI->hTask));
  1323. // if we stopped to do this, then restart whatever we were doing
  1324. if (npMCI->dwFlags & MCIAVI_UPDATING) {
  1325. // We stopped to do this...
  1326. EnterWinCrit(npMCI);
  1327. // Turn the lose audio flag on so that when we restart we do not
  1328. // try and pick up the wave device. The flag will be reset in
  1329. // SetUpAudio.
  1330. npMCI->dwFlags |= MCIAVI_LOSEAUDIO;
  1331. // Hint that we would like sound again
  1332. npMCI->dwFlags |= MCIAVI_LOSTAUDIO;
  1333. LeaveWinCrit(npMCI);
  1334. OnTask_RestartAgain(npMCI, TRUE);
  1335. Assert(!(npMCI->dwFlags & MCIAVI_LOSEAUDIO));
  1336. // The flag has been reset by SetUpAudio
  1337. // By using MCIAVI_LOSEAUDIO we do not have to change the state of
  1338. // the MCIAVI_PLAYAUDIO flag. This is goodness as that flag controls
  1339. // the mute state - and that is independent of the availability of a
  1340. // wave device, activation and/or deactivation.
  1341. } else {
  1342. TaskReturns(npMCI, 0);
  1343. }
  1344. }
  1345. void OnTask_WaveReturn(NPMCIGRAPHIC npMCI) {
  1346. // Turn off the flag that caused us to get called.
  1347. // Note: if the audio device is still unavailable, this flag will get
  1348. // turned on again when we fail to open the device.
  1349. npMCI->dwFlags &= ~MCIAVI_LOSTAUDIO;
  1350. DPF2(("OnTask_WaveReturn... pick up the audio\n"));
  1351. // if we stopped to do this, then restart whatever we were doing
  1352. if (npMCI->dwFlags & MCIAVI_UPDATING) {
  1353. OnTask_RestartAgain(npMCI, TRUE);
  1354. } else {
  1355. TaskReturns(npMCI, 0);
  1356. }
  1357. }
  1358. BOOL OnTask_WaveStealDuringPlay(NPMCIGRAPHIC npMCI) {
  1359. DPF2(("OnTask_WaveStealDuringPlay, '%ls' hTask=%04X\n", (LPSTR)npMCI->szFilename, npMCI->hTask));
  1360. /* If we do not have the audio, just return. */
  1361. if (npMCI->hWave == 0) {
  1362. TaskReturns(npMCI, 0);
  1363. return FALSE;
  1364. }
  1365. /* Stop before changing sound status */
  1366. OnTask_StopTemporarily(npMCI);
  1367. return(TRUE);
  1368. }
  1369. /*
  1370. * A wave device may have become available. Stop and try to pick it up.
  1371. */
  1372. BOOL OnTask_WaveReturnDuringPlay(NPMCIGRAPHIC npMCI) {
  1373. /* If there's no audio, just return. */
  1374. if (npMCI->nAudioStreams == 0) {
  1375. npMCI->dwFlags &= ~MCIAVI_LOSTAUDIO;
  1376. TaskReturns(npMCI, 0);
  1377. return FALSE;
  1378. }
  1379. /* Stop before changing sound status */
  1380. OnTask_StopTemporarily(npMCI);
  1381. return(TRUE);
  1382. }
  1383. BOOL
  1384. OnTask_MuteDuringPlay(NPMCIGRAPHIC npMCI)
  1385. {
  1386. // If npMCI->lParam is TRUE, this means that we are to mute the
  1387. // device - hence turn off the PLAYAUDIO flag.
  1388. DWORD fPlayAudio = (DWORD)((BOOL) npMCI->lParam ? 0 : MCIAVI_PLAYAUDIO);
  1389. /* If there's no audio, just return. Should this be an error? */
  1390. if (npMCI->nAudioStreams == 0) {
  1391. TaskReturns(npMCI, 0);
  1392. return FALSE;
  1393. }
  1394. /* If the mute state isn't changing, don't do anything. */
  1395. if ( (npMCI->dwFlags & MCIAVI_PLAYAUDIO) == fPlayAudio) {
  1396. TaskReturns(npMCI, 0);
  1397. return FALSE;
  1398. }
  1399. DPF2(("DeviceMute, fPlayAudio = %x, npMCI=%8x\n", fPlayAudio, npMCI));
  1400. /* Stop before changing mute */
  1401. OnTask_StopTemporarily(npMCI);
  1402. return TRUE;
  1403. }
  1404. void
  1405. OnTask_Mute(NPMCIGRAPHIC npMCI)
  1406. {
  1407. // If npMCI->lParam is TRUE, this means that we are to mute the
  1408. // device - hence turn off the PLAYAUDIO flag.
  1409. // We do not bother to check a change in state. That is only
  1410. // relevant if we are already playing when we only want to stop
  1411. // for a change in state.
  1412. BOOL fMute = (BOOL)npMCI->lParam;
  1413. /* If there's no audio, just return. Should this be an error? */
  1414. if (npMCI->nAudioStreams != 0) {
  1415. EnterWinCrit(npMCI);
  1416. if (fMute)
  1417. npMCI->dwFlags &= ~MCIAVI_PLAYAUDIO;
  1418. else
  1419. npMCI->dwFlags |= MCIAVI_PLAYAUDIO;
  1420. LeaveWinCrit(npMCI);
  1421. }
  1422. // if we stopped to do this, then restart whatever we were doing
  1423. if (npMCI->dwFlags & MCIAVI_UPDATING) {
  1424. OnTask_RestartAgain(npMCI, TRUE);
  1425. } else {
  1426. TaskReturns(npMCI, 0);
  1427. }
  1428. }
  1429. // all access to the hWave *must* be restricted to the thread that created
  1430. // the wave device. So even getting the volume must be done on the
  1431. // worker thread only
  1432. //
  1433. // this function gets the current volume setting and stores it in
  1434. // npMCI->dwVolume
  1435. DWORD
  1436. InternalGetVolume(NPMCIGRAPHIC npMCI)
  1437. {
  1438. DWORD dw;
  1439. DWORD dwVolume;
  1440. if (npMCI->hWave) {
  1441. // Get the current audio volume....
  1442. dw = waveOutMessage(npMCI->hWave, WODM_GETVOLUME,
  1443. (DWORD) (DWORD FAR *)&dwVolume, 0);
  1444. } else if (!(npMCI->dwFlags & MCIAVI_VOLUMESET)) {
  1445. // We have no device open, and the user hasn't chosen a
  1446. // volume yet.
  1447. //
  1448. // Try to find out what the current "default" volume is.
  1449. //
  1450. // I really doubt zero is the current volume, try to work
  1451. // with broken cards like the stupid windows sound system.
  1452. //
  1453. dw = waveOutGetVolume((HWAVEOUT)(UINT)WAVE_MAPPER, &dwVolume);
  1454. if ((dw != 0) || (dwVolume != 0)) {
  1455. dw = waveOutGetVolume((HWAVEOUT)0, &dwVolume);
  1456. }
  1457. // don't accept default volume of 0
  1458. if ((dwVolume == 0) && (dw == 0)) {
  1459. dw = MCIERR_NONAPPLICABLE_FUNCTION;
  1460. }
  1461. }
  1462. if (dw == 0) {
  1463. npMCI->dwVolume = MAKELONG((UINT)muldiv32(LOWORD(dwVolume), 500L, 32768L),
  1464. (UINT)muldiv32(HIWORD(dwVolume), 500L, 32768L));
  1465. }
  1466. return dw;
  1467. }
  1468. DWORD
  1469. InternalSetVolume(NPMCIGRAPHIC npMCI, DWORD dwVolume)
  1470. {
  1471. DWORD dw = 0;
  1472. npMCI->dwVolume = dwVolume;
  1473. EnterWinCrit(npMCI);
  1474. npMCI->dwFlags |= MCIAVI_VOLUMESET;
  1475. LeaveWinCrit(npMCI);
  1476. /* clear flag to emulate volume */;
  1477. npMCI->fEmulatingVolume = FALSE;
  1478. /* If there's no audio, just return. Should this be an error? */
  1479. if (npMCI->nAudioStreams != 0) {
  1480. if (npMCI->hWave) {
  1481. WORD wLeft;
  1482. WORD wRight;
  1483. if (LOWORD(dwVolume) >= 1000)
  1484. wLeft = 0xFFFF;
  1485. else
  1486. wLeft = (WORD) muldiv32(LOWORD(dwVolume), 32768L, 500L);
  1487. if (HIWORD(dwVolume) >= 1000)
  1488. wRight = 0xFFFF;
  1489. else
  1490. wRight = (WORD) muldiv32(HIWORD(dwVolume), 32768L, 500L);
  1491. // !!! Support left and right volume?
  1492. dw = waveOutMessage(npMCI->hWave, WODM_SETVOLUME,
  1493. MAKELONG(wLeft, wRight), 0);
  1494. if (dw != MMSYSERR_NOERROR && LOWORD(dwVolume) != 500) {
  1495. npMCI->fEmulatingVolume = TRUE;
  1496. BuildVolumeTable(npMCI);
  1497. }
  1498. dw = 0;
  1499. }
  1500. }
  1501. return dw;
  1502. }
  1503. INLINE void
  1504. OnTask_SetVolume(NPMCIGRAPHIC npMCI)
  1505. {
  1506. DWORD dwVolume = (DWORD) npMCI->lParam;
  1507. TaskReturns(npMCI, InternalSetVolume(npMCI, dwVolume));
  1508. }
  1509. void OnTask_SetAudioStream(NPMCIGRAPHIC npMCI)
  1510. {
  1511. UINT wAudioStream = npMCI->dwParamFlags;
  1512. int stream;
  1513. /* If there's no audio, we're done. Should this be an error? */
  1514. if (npMCI->nAudioStreams != 0) {
  1515. for (stream = 0; stream < npMCI->streams; stream++) {
  1516. if (SH(stream).fccType == streamtypeAUDIO) {
  1517. --wAudioStream;
  1518. if (wAudioStream == 0)
  1519. break;
  1520. }
  1521. }
  1522. Assert(stream < npMCI->streams);
  1523. npMCI->psiAudio = SI(stream);
  1524. npMCI->nAudioStream = stream;
  1525. }
  1526. // if we stopped to do this, then restart whatever we were doing
  1527. if (npMCI->dwFlags & MCIAVI_UPDATING) {
  1528. OnTask_RestartAgain(npMCI, TRUE);
  1529. } else {
  1530. TaskReturns(npMCI, 0);
  1531. }
  1532. }
  1533. void
  1534. OnTask_SetVideoStream(NPMCIGRAPHIC npMCI)
  1535. {
  1536. UINT uStream = npMCI->dwParamFlags;
  1537. BOOL fOn = (BOOL) npMCI->lParam;
  1538. DWORD dw = 0L;
  1539. int stream;
  1540. STREAMINFO *psi;
  1541. //
  1542. // find the Nth non-audio, non-error stream
  1543. //
  1544. for (stream = 0; stream < npMCI->streams; stream++) {
  1545. psi = SI(stream);
  1546. if (psi->sh.fccType == streamtypeAUDIO)
  1547. continue;
  1548. if (psi->dwFlags & STREAM_ERROR)
  1549. continue;
  1550. if (--uStream == 0)
  1551. break;
  1552. }
  1553. if (stream == npMCI->streams) {
  1554. dw = MCIERR_OUTOFRANGE;
  1555. } else {
  1556. if (fOn)
  1557. psi->dwFlags |= STREAM_ENABLED;
  1558. else
  1559. psi->dwFlags &= ~STREAM_ENABLED;
  1560. if (fOn && psi->sh.fccType == streamtypeVIDEO) {
  1561. //!!! should we change the master timebase?
  1562. DOUT("Setting main video stream\n");
  1563. #if 0
  1564. //
  1565. // the master video stream is too special cased to change!
  1566. //
  1567. npMCI->psiVideo = psi;
  1568. npMCI->nVideoStream = stream;
  1569. #endif
  1570. }
  1571. if (!fOn && npMCI->nVideoStream == stream) {
  1572. DOUT("Turning off main video stream\n");
  1573. npMCI->dwFlags &= ~MCIAVI_SHOWVIDEO;
  1574. }
  1575. //
  1576. // now we turn MCIAVI_SHOWVIDEO off if no video/other streams
  1577. // are enabled.
  1578. //
  1579. npMCI->dwFlags &= ~MCIAVI_SHOWVIDEO; // assume off.
  1580. for (stream = 0; stream < npMCI->streams; stream++) {
  1581. psi = SI(stream);
  1582. if (psi->sh.fccType == streamtypeAUDIO)
  1583. continue;
  1584. if (psi->dwFlags & STREAM_ERROR)
  1585. continue;
  1586. if (!(psi->dwFlags & STREAM_ENABLED))
  1587. continue;
  1588. // at least one stream is enabled show "video"
  1589. npMCI->dwFlags |= MCIAVI_SHOWVIDEO;
  1590. }
  1591. if (!(npMCI->dwFlags & MCIAVI_SHOWVIDEO))
  1592. DOUT("All streams off\n");
  1593. }
  1594. // if we stopped to do this, then restart whatever we were doing
  1595. if ( (dw == 0) && (npMCI->dwFlags & MCIAVI_UPDATING)) {
  1596. OnTask_RestartAgain(npMCI, TRUE);
  1597. } else {
  1598. TaskReturns(npMCI, dw);
  1599. }
  1600. }
  1601. /***************************************************************************
  1602. *
  1603. ***************************************************************************/
  1604. static void MapRect(RECT *prc, RECT*prcIn, RECT *prcFrom, RECT *prcTo)
  1605. {
  1606. if (IsRectEmpty(prcFrom)) {
  1607. SetRectEmpty(prc);
  1608. }
  1609. else {
  1610. prc->left = prcTo->left + MulDiv(prcIn->left - prcFrom->left, prcTo->right - prcTo->left, prcFrom->right - prcFrom->left);
  1611. prc->top = prcTo->top + MulDiv(prcIn->top - prcFrom->top, prcTo->bottom - prcTo->top, prcFrom->bottom - prcFrom->top);
  1612. prc->right = prcTo->left + MulDiv(prcIn->right - prcFrom->left, prcTo->right - prcTo->left, prcFrom->right - prcFrom->left);
  1613. prc->bottom= prcTo->top + MulDiv(prcIn->bottom- prcFrom->top, prcTo->bottom - prcTo->top, prcFrom->bottom - prcFrom->top);
  1614. }
  1615. }
  1616. /***************************************************************************
  1617. *
  1618. ***************************************************************************/
  1619. static void MapStreamRects(NPMCIGRAPHIC npMCI)
  1620. {
  1621. int i;
  1622. //
  1623. // now set the source and dest rects for each stream.
  1624. //
  1625. for (i=0; i<npMCI->streams; i++)
  1626. {
  1627. //
  1628. // make sure the stream rect is in bounds
  1629. //
  1630. IntersectRect(&SI(i)->rcSource, &SH(i).rcFrame, &npMCI->rcSource);
  1631. //
  1632. // now map the stream source rect onto the destination
  1633. //
  1634. MapRect(&SI(i)->rcDest, &SI(i)->rcSource, &npMCI->rcSource, &npMCI->rcDest);
  1635. //
  1636. // make the stream source RECT (rcSource) relative to the
  1637. // stream rect (rcFrame)
  1638. //
  1639. OffsetRect(&SI(i)->rcSource,-SH(i).rcFrame.left,-SH(i).rcFrame.top);
  1640. }
  1641. }
  1642. //
  1643. // try to set the dest or source rect without stopping play.
  1644. // called both at stop time and at play time
  1645. //
  1646. // returns TRUE if stop needed, or else FALSE if all handled.
  1647. // lpdwErr is set to a non-zero error if any error occured (in which case
  1648. // FALSE will be returned.
  1649. //
  1650. BOOL
  1651. TryPutRect(NPMCIGRAPHIC npMCI, DWORD dwFlags, LPRECT lprc, LPDWORD lpdwErr)
  1652. {
  1653. RECT rc;
  1654. PRECT prcPut;
  1655. DWORD dw = 0;
  1656. // assume no error
  1657. *lpdwErr = 0;
  1658. if (dwFlags & MCI_DGV_PUT_DESTINATION) {
  1659. DPF2(("DevicePut: destination [%d, %d, %d, %d]\n", *lprc));
  1660. prcPut = &npMCI->rcDest;
  1661. } else {
  1662. DPF2(("DevicePut: source [%d, %d, %d, %d]\n", *lprc));
  1663. prcPut = &npMCI->rcSource;
  1664. //
  1665. // make sure source rectangle is in range.
  1666. //
  1667. // !!!should we return a error, or just fix the rectangle???
  1668. //
  1669. // ?? Why do we use an intermediate structure?
  1670. rc = npMCI->rcMovie;
  1671. IntersectRect(lprc, &rc, lprc); // fix up the passed rect.
  1672. }
  1673. //
  1674. // check for a bogus rect. either a NULL or inverted rect is considered
  1675. // invalid.
  1676. //
  1677. // !!!NOTE we should handle a inverted rect (mirrored stretch)
  1678. //
  1679. if (lprc->left >= lprc->right ||
  1680. lprc->top >= lprc->bottom) {
  1681. // this is fine if there are no video streams
  1682. if (npMCI->nVideoStreams <= 0) {
  1683. // no video so all ok
  1684. return FALSE;
  1685. }
  1686. DPF2(("DevicePut: invalid rectangle [%d, %d, %d, %d]\n", *lprc));
  1687. *lpdwErr = MCIERR_OUTOFRANGE;
  1688. return FALSE;
  1689. }
  1690. /* make sure the rect changed */
  1691. if (EqualRect(prcPut,lprc)) {
  1692. return FALSE;
  1693. }
  1694. InvalidateRect(npMCI->hwndPlayback, &npMCI->rcDest, TRUE);
  1695. rc = *prcPut; /* save it */
  1696. *prcPut = *lprc; /* change it */
  1697. InvalidateRect(npMCI->hwndPlayback, &npMCI->rcDest, FALSE);
  1698. /* have both the dest and source been set? */
  1699. if (IsRectEmpty(&npMCI->rcDest) || IsRectEmpty(&npMCI->rcSource)) {
  1700. return FALSE;
  1701. }
  1702. MapStreamRects(npMCI);
  1703. StreamInvalidate(npMCI, NULL); // invalidate the world
  1704. if (npMCI->wTaskState == TASKIDLE) {
  1705. DPF2(("TryPutRect: Idle, force DrawBegin on update\n"));
  1706. npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN;
  1707. }
  1708. else {
  1709. BOOL fRestart;
  1710. //
  1711. // we dont need to start/stop just begin again.
  1712. //
  1713. DPF2(("TryPutRect: Calling DrawBegin()\n"));
  1714. if (!DrawBegin(npMCI, &fRestart)) {
  1715. *lpdwErr = npMCI->dwTaskError;
  1716. return FALSE;
  1717. }
  1718. if (!DoStreamUpdate(npMCI, FALSE)) {
  1719. DPF(("TryPutRect: Failed update, forcing restart....\n"));
  1720. npMCI->lFrameDrawn = (- (LONG) npMCI->wEarlyRecords) - 1;
  1721. fRestart = TRUE;
  1722. }
  1723. if (fRestart) {
  1724. // restart needed
  1725. return TRUE;
  1726. }
  1727. }
  1728. // all ok
  1729. return FALSE;
  1730. }
  1731. void
  1732. OnTask_Put(NPMCIGRAPHIC npMCI)
  1733. {
  1734. DWORD dwFlags = npMCI->dwParamFlags;
  1735. LPRECT lprc = (LPRECT) npMCI->lParam;
  1736. DWORD dw = 0;
  1737. if (TryPutRect(npMCI, dwFlags, lprc, &dw)) {
  1738. // what to do now? It says we need to stop, but we
  1739. // are stopped.
  1740. TaskReturns(npMCI, MCIERR_DEVICE_NOT_READY);
  1741. return;
  1742. }
  1743. // if we stopped to do this, then restart whatever we were doing
  1744. if ((dw == 0) && (npMCI->dwFlags & MCIAVI_UPDATING)) {
  1745. // !!! We used to call InitDecompress here...
  1746. npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN;
  1747. OnTask_RestartAgain(npMCI, TRUE);
  1748. } else {
  1749. TaskReturns(npMCI, dw);
  1750. }
  1751. }
  1752. BOOL
  1753. OnTask_PutDuringPlay(NPMCIGRAPHIC npMCI)
  1754. {
  1755. DWORD dwFlags = npMCI->dwParamFlags;
  1756. LPRECT lprc = (LPRECT) npMCI->lParam;
  1757. DWORD dw = 0;
  1758. if (TryPutRect(npMCI, dwFlags, lprc, &dw)) {
  1759. // need to stop to handle this one.
  1760. // !!! Set a flag here to prevent any more drawing
  1761. npMCI->fNoDrawing = TRUE;
  1762. OnTask_StopTemporarily(npMCI);
  1763. return TRUE;
  1764. }
  1765. // handled ok or error - no stop needed
  1766. TaskReturns(npMCI, dw);
  1767. return FALSE;
  1768. }
  1769. void OnTask_Palette(NPMCIGRAPHIC npMCI)
  1770. {
  1771. HPALETTE hpal = (HPALETTE)npMCI->lParam;
  1772. // Remember this for later.
  1773. npMCI->hpal = hpal;
  1774. npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN;
  1775. InvalidateRect(npMCI->hwndPlayback, NULL, TRUE);
  1776. // !!! Do we need to stop and restart here?
  1777. // Answer: probably not, because if they care about the palette not being
  1778. // messed up, they probably never allowed us to be shown at all.
  1779. TaskReturns(npMCI, 0);
  1780. return;
  1781. }
  1782. void OnTask_PaletteColor(NPMCIGRAPHIC npMCI)
  1783. {
  1784. DWORD index = (DWORD)npMCI->lParam;
  1785. DWORD color = (DWORD)npMCI->dwParamFlags;
  1786. // !!! Do we need to stop and restart here?
  1787. // Answer: probably not, because if they care about the palette not being
  1788. // messed up, they probably never allowed us to be shown at all.
  1789. // Note: chicago code does stop... but they stop for most things.
  1790. // (it would be cleaner to stop and restart...)
  1791. // Pound the new color into the old format.
  1792. ((DWORD FAR *) ((BYTE FAR *) npMCI->pbiFormat +
  1793. npMCI->pbiFormat->biSize))[index] = color;
  1794. ((DWORD FAR *) npMCI->argb)[index] = color;
  1795. npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN;
  1796. InvalidateRect(npMCI->hwndPlayback, NULL, TRUE);
  1797. TaskReturns(npMCI, 0);
  1798. return;
  1799. }
  1800. /*
  1801. * OnTask_ProcessRequest
  1802. *
  1803. * Process a request on the worker thread. Set hEventResponse if completed
  1804. * in error, or once the request has been completed (in the case of async
  1805. * requests such as play, set the event once play has begun ok
  1806. *
  1807. * return TRUE if it is time for the thread to exit, or else false.
  1808. *
  1809. */
  1810. BOOL
  1811. OnTask_ProcessRequest(NPMCIGRAPHIC npMCI)
  1812. {
  1813. switch(npMCI->message) {
  1814. case AVI_CLOSE:
  1815. // release the requesting thread so he can go and wait on the
  1816. // worker thread exit
  1817. TaskReturns(npMCI, 0);
  1818. // now go and exit
  1819. return TRUE;
  1820. case AVI_RESUME:
  1821. // same as play, except that repeat and reverse flags need
  1822. // to be set on worker thread
  1823. npMCI->dwParamFlags |=
  1824. ((npMCI->dwFlags & MCIAVI_REVERSE)? MCI_DGV_PLAY_REVERSE : 0);
  1825. // fall through
  1826. case AVI_PLAY:
  1827. OnTask_Play(npMCI);
  1828. break;
  1829. case AVI_STOP:
  1830. npMCI->dwFlags &= ~MCIAVI_REPEATING; // Give the wave device away
  1831. TaskReturns(npMCI, 0);
  1832. break;
  1833. case AVI_REALIZE:
  1834. OnTask_Realize(npMCI);
  1835. break;
  1836. case AVI_UPDATE:
  1837. OnTask_Update(npMCI);
  1838. break;
  1839. case AVI_PAUSE:
  1840. // not playing, so same as cue to current frame
  1841. OnTask_Cue(npMCI, npMCI->dwParamFlags | MCI_TO, npMCI->lCurrentFrame);
  1842. break;
  1843. case AVI_CUE:
  1844. OnTask_Cue(npMCI, npMCI->dwParamFlags, npMCI->lParam);
  1845. break;
  1846. case AVI_SEEK:
  1847. OnTask_Seek(npMCI);
  1848. break;
  1849. case AVI_WINDOW:
  1850. OnTask_SetWindow(npMCI);
  1851. break;
  1852. case AVI_MUTE:
  1853. OnTask_Mute(npMCI);
  1854. break;
  1855. case AVI_SETSPEED:
  1856. OnTask_SetSpeed(npMCI);
  1857. break;
  1858. case AVI_SETVOLUME:
  1859. OnTask_SetVolume(npMCI);
  1860. break;
  1861. case AVI_GETVOLUME:
  1862. TaskReturns(npMCI, InternalGetVolume(npMCI));
  1863. break;
  1864. case AVI_AUDIOSTREAM:
  1865. OnTask_SetAudioStream(npMCI);
  1866. break;
  1867. case AVI_VIDEOSTREAM:
  1868. OnTask_SetVideoStream(npMCI);
  1869. break;
  1870. case AVI_PUT:
  1871. OnTask_Put(npMCI);
  1872. break;
  1873. case AVI_PALETTE:
  1874. OnTask_Palette(npMCI);
  1875. break;
  1876. case AVI_PALETTECOLOR:
  1877. OnTask_PaletteColor(npMCI);
  1878. break;
  1879. case AVI_WAVESTEAL:
  1880. OnTask_WaveSteal(npMCI);
  1881. break;
  1882. case AVI_WAVERETURN:
  1883. OnTask_WaveReturn(npMCI);
  1884. break;
  1885. default:
  1886. TaskReturns(npMCI, MCIERR_UNSUPPORTED_FUNCTION);
  1887. break;
  1888. }
  1889. return FALSE;
  1890. }
  1891. // OnTask_PeekRequest
  1892. //
  1893. // called from aviTaskCheckRequests() to process a message at play time.
  1894. // if the message requires a stop, then this function returns TRUE and
  1895. // leaves the message unprocessed.
  1896. //
  1897. // otherwise the message is fully processed. This must include resetting
  1898. // hEventSend
  1899. //
  1900. INLINE STATICFN BOOL
  1901. OnTask_PeekRequest(NPMCIGRAPHIC npMCI)
  1902. {
  1903. switch(npMCI->message) {
  1904. // always need to stop
  1905. case AVI_CLOSE:
  1906. case AVI_STOP:
  1907. npMCI->dwFlags &= ~MCIAVI_REPEATING; // Give the wave device away
  1908. return TRUE;
  1909. // may need to stop
  1910. case AVI_RESUME:
  1911. // same as play, except that repeat and reverse flags need
  1912. // to be set on worker thread
  1913. npMCI->dwParamFlags |=
  1914. ((npMCI->dwFlags & MCIAVI_REPEATING)? MCI_DGV_PLAY_REPEAT : 0) |
  1915. ((npMCI->dwFlags & MCIAVI_REVERSE)? MCI_DGV_PLAY_REVERSE : 0);
  1916. // fall through
  1917. case AVI_PLAY:
  1918. return OnTask_PlayDuringPlay(npMCI);
  1919. case AVI_UPDATE:
  1920. return OnTask_UpdateDuringPlay(npMCI);
  1921. case AVI_SEEK:
  1922. return OnTask_SeekDuringPlay(npMCI);
  1923. case AVI_CUE:
  1924. return OnTask_CueDuringPlay(npMCI);
  1925. case AVI_MUTE:
  1926. return OnTask_MuteDuringPlay(npMCI);
  1927. case AVI_WAVESTEAL:
  1928. return OnTask_WaveStealDuringPlay(npMCI);
  1929. case AVI_WAVERETURN:
  1930. return OnTask_WaveReturnDuringPlay(npMCI);
  1931. case AVI_SETSPEED:
  1932. return OnTask_SetSpeedDuringPlay(npMCI);
  1933. case AVI_PUT:
  1934. return OnTask_PutDuringPlay(npMCI);
  1935. // need temporary stop
  1936. case AVI_WINDOW:
  1937. case AVI_AUDIOSTREAM:
  1938. case AVI_VIDEOSTREAM:
  1939. OnTask_StopTemporarily(npMCI);
  1940. return TRUE;
  1941. // never need to stop
  1942. case AVI_REALIZE:
  1943. OnTask_Realize(npMCI);
  1944. break;
  1945. case AVI_PAUSE:
  1946. OnTask_PauseDuringPlay(npMCI);
  1947. break;
  1948. case AVI_SETVOLUME:
  1949. OnTask_SetVolume(npMCI);
  1950. break;
  1951. case AVI_GETVOLUME:
  1952. TaskReturns(npMCI, InternalGetVolume(npMCI));
  1953. break;
  1954. case AVI_PALETTE:
  1955. OnTask_Palette(npMCI);
  1956. break;
  1957. case AVI_PALETTECOLOR:
  1958. OnTask_PaletteColor(npMCI);
  1959. break;
  1960. default:
  1961. TaskReturns(npMCI, MCIERR_UNSUPPORTED_FUNCTION);
  1962. break;
  1963. }
  1964. return FALSE;
  1965. }
  1966. /*
  1967. * This routine is called from the IDLE loop and at key points while
  1968. * playing. If it is possible to service the request, the state of the
  1969. * device is updated and the request flag is cleared.
  1970. *
  1971. * If the request cannot be handled now (e.g. while playing) the flag
  1972. * is not set and we will be called again (e.g. when idle).
  1973. *
  1974. * If we need to stop to service the request (i.e. to regain the sound
  1975. * device) we return TRUE. In all other cases we return FALSE. The
  1976. * return value is only checked if we are actually playing.
  1977. */
  1978. STATICFN void OnTask_WinProcRequests(NPMCIGRAPHIC npMCI, BOOL bPlaying)
  1979. {
  1980. DWORD requests;
  1981. EnterWinCrit(npMCI);
  1982. // grab the request bits now, so we don't need to hold the
  1983. // critsec while servicing them.
  1984. // any that are not cleared will be or-ed back in at the end
  1985. requests = npMCI->dwWinProcRequests;
  1986. npMCI->dwWinProcRequests = 0;
  1987. LeaveWinCrit(npMCI);
  1988. if (requests & WINPROC_STOP) {
  1989. requests &= ~WINPROC_STOP;
  1990. npMCI->dwFlags |= MCIAVI_STOP;
  1991. }
  1992. if (requests & WINPROC_MUTE) {
  1993. if (bPlaying) {
  1994. OnTask_StopTemporarily(npMCI);
  1995. } else {
  1996. // toggle audio flag
  1997. npMCI->dwFlags ^= MCIAVI_PLAYAUDIO;
  1998. requests &= ~WINPROC_MUTE;
  1999. }
  2000. }
  2001. if (requests & WINPROC_SOUND) {
  2002. // We might be able to pick up the sound. This is only of interest
  2003. // if we are currently playing, do not have a sound device, and want
  2004. // the audio.
  2005. if (bPlaying && (NULL == npMCI->hWave) && (MCIAVI_PLAYAUDIO & npMCI->dwFlags)) {
  2006. OnTask_StopTemporarily(npMCI);
  2007. } else {
  2008. // We have finished this request. Make sure we try and
  2009. // get sound when we restart
  2010. requests &= ~WINPROC_SOUND;
  2011. npMCI->dwFlags &= ~MCIAVI_LOSEAUDIO;
  2012. }
  2013. }
  2014. #ifdef REMOTESTEAL
  2015. if (requests & WINPROC_SILENT) {
  2016. extern HWND hwndWantAudio;
  2017. DPF2(("WINPROC_SILENT request made, bPlaying=%x\n", bPlaying));
  2018. // If we are playing, and we have a wave device, stop.
  2019. // When we are recalled, we will start again without the wave device.
  2020. if (bPlaying && npMCI->hWave) {
  2021. OnTask_StopTemporarily(npMCI);
  2022. // Stopping will cause the wave device to be released, which
  2023. // means a message will be posted to whoever wanted the wave
  2024. // device
  2025. } else {
  2026. // If we are playing, we do not have a wave device, and we
  2027. // do not want to stop.
  2028. // Otherwise we want to lose our wave device.
  2029. // Either way, we will finish with WINPROC_SILENT on this pass
  2030. requests &= ~WINPROC_SILENT;
  2031. hwndWantAudio = 0; // In case we did not have to stop
  2032. if (!bPlaying) {
  2033. // Remember we lost audio, and start again without audio
  2034. npMCI->dwFlags |= MCIAVI_LOSTAUDIO;
  2035. npMCI->dwFlags |= MCIAVI_LOSEAUDIO;
  2036. }
  2037. }
  2038. }
  2039. #endif
  2040. if (requests & WINPROC_RESETDEST) {
  2041. RECT rc;
  2042. DWORD dw;
  2043. if (npMCI->hwndPlayback &&
  2044. npMCI->hwndPlayback == npMCI->hwndDefault &&
  2045. (npMCI->dwOptionFlags & MCIAVIO_STRETCHTOWINDOW)) {
  2046. GetClientRect(npMCI->hwndPlayback, &rc);
  2047. } else if (npMCI->streams > 0) {
  2048. rc = npMCI->rcMovie;
  2049. if (npMCI->dwOptionFlags & MCIAVIO_ZOOMBY2) {
  2050. rc.right *= 2;
  2051. rc.bottom *= 2;
  2052. }
  2053. }
  2054. if (TryPutRect(npMCI, MCI_DGV_PUT_DESTINATION, &rc, &dw) && bPlaying) {
  2055. OnTask_StopTemporarily(npMCI);
  2056. } else {
  2057. requests &= ~WINPROC_RESETDEST;
  2058. }
  2059. }
  2060. if (requests & WINPROC_ACTIVE) {
  2061. // We are being made active. The only extra work we must do
  2062. // is grab the wave device - if we have ever lost it.
  2063. // If we are playing, and we do not have the audio, and we want
  2064. // the audio...
  2065. if (bPlaying
  2066. && (npMCI->hWave == 0)
  2067. && (npMCI->dwFlags & MCIAVI_PLAYAUDIO)) {
  2068. // Let's try and make the sound active by stealing the wave device
  2069. // Must stop before trying to reset the sound
  2070. if (StealWaveDevice(npMCI)) {
  2071. OnTask_StopTemporarily(npMCI);
  2072. // Force ourselves to be called again. Doing it this way
  2073. // means that we will be recalled. We cannot rely on
  2074. // WINPROC_ACTIVE staying around. A deactivation could
  2075. // cause the flag to be cleared
  2076. requests |= WINPROC_SOUND;
  2077. }
  2078. } else {
  2079. // We had not lost the wave device...
  2080. // OR we are playing silently, and so there is no point
  2081. // in trying to steal it.
  2082. // We are finished.
  2083. }
  2084. // Clear WINPROC_ACTIVE - all processing done.
  2085. // Note: we might have set WINPROC_SOUND, which will cause this
  2086. // routine to be recalled. Once recalled, then playing can restart
  2087. requests &= ~ WINPROC_ACTIVE;
  2088. } else { // We never have both INACTIVE and ACTIVE at the same time
  2089. if (requests & WINPROC_INACTIVE) {
  2090. //!!!need to support this
  2091. requests &= ~WINPROC_INACTIVE;
  2092. }
  2093. }
  2094. EnterWinCrit(npMCI); // Do we really need this one here??
  2095. if (requests & WINPROC_UPDATE) {
  2096. if (bPlaying) {
  2097. npMCI->dwFlags |= MCIAVI_NEEDUPDATE;
  2098. } else {
  2099. HDC hdc;
  2100. // don't do this if the window is now hidden
  2101. // or showstage will be called with the critsec and deadlock
  2102. if (IsWindowVisible(npMCI->hwndPlayback)) {
  2103. EnterHDCCrit(npMCI);
  2104. npMCI->bDoingWinUpdate = TRUE;
  2105. hdc = GetDC(npMCI->hwndPlayback);
  2106. Assert(hdc);
  2107. Internal_Update(npMCI, MCI_DGV_UPDATE_PAINT, hdc, NULL);
  2108. ReleaseDC(npMCI->hwndPlayback, hdc);
  2109. npMCI->bDoingWinUpdate = FALSE;
  2110. LeaveHDCCrit(npMCI);
  2111. }
  2112. }
  2113. requests &= ~WINPROC_UPDATE;
  2114. }
  2115. if (requests & WINPROC_REALIZE) {
  2116. EnterHDCCrit(npMCI);
  2117. InternalRealize(npMCI);
  2118. LeaveHDCCrit(npMCI);
  2119. requests &= ~ WINPROC_REALIZE;
  2120. }
  2121. // or back the bits we didn't clear
  2122. npMCI->dwWinProcRequests |= requests;
  2123. // if we processed all the bits (and no new bits were set)
  2124. if (! npMCI->dwWinProcRequests) {
  2125. ResetEvent(npMCI->heWinProcRequest);
  2126. }
  2127. LeaveWinCrit(npMCI);
  2128. }
  2129. /***************************************************************************
  2130. *
  2131. * @doc INTERNAL MCIAVI
  2132. *
  2133. * @api void| aviTaskCheckRequests | called on the worker thread at least once per
  2134. *
  2135. * frame. We use this to check for requests from the user thread.
  2136. *
  2137. *
  2138. ***************************************************************************/
  2139. void NEAR PASCAL aviTaskCheckRequests(NPMCIGRAPHIC npMCI)
  2140. {
  2141. HANDLE hWaiter;
  2142. if (WaitForSingleObject(npMCI->hEventSend, 0) == WAIT_OBJECT_0) {
  2143. // there is a request
  2144. Assert(npMCI->message != 0);
  2145. // check the waiter
  2146. // if this is an async request with wait, we need to set hWaiter
  2147. // so that hEventAllDone is set correctly. If we stop
  2148. // and process this message in the idle loop, then we don't want to
  2149. // set hWaiter here, or EventAllDone could be signalled when we
  2150. // stop - before we've even started this request.
  2151. // so we need to check the validity (no waiting if another thread is
  2152. // waiting) and pick up the waiter and bDelayed while the critsec
  2153. // is still held, but only set hWaiter if the request was processed
  2154. // here.
  2155. // no - that leaves a timing window when hWaiter is not set and the
  2156. // critsec is not held. Set hWaiter, but be prepared to unset it
  2157. // if we postpone processing during the idle loop (in which case,
  2158. // the waiter will hold the critsec until we have stopped).
  2159. hWaiter = npMCI->hWaiter;
  2160. if (npMCI->bDelayedComplete) {
  2161. if (npMCI->hWaiter && (npMCI->hWaiter != npMCI->hRequestor)) {
  2162. TaskReturns(npMCI, MCIERR_DEVICE_NOT_READY);
  2163. return;
  2164. } else {
  2165. DPF2(("Replacing hWaiter in aviTaskCheckRequests... was %x, now %x\n", npMCI->hWaiter, npMCI->hRequestor));
  2166. npMCI->hWaiter = npMCI->hRequestor;
  2167. }
  2168. }
  2169. DPF2(("peek %d [%x] ...", npMCI->message, npMCI->hRequestor));
  2170. if (OnTask_PeekRequest(npMCI)) {
  2171. // we need to stop
  2172. // must be set on WORKER THREAD ONLY
  2173. npMCI->dwFlags |= MCIAVI_STOP;
  2174. DPF2(("Need to stop - replacing hWaiter (was %x, now %x)\n", npMCI->hWaiter, hWaiter));
  2175. // replace hWaiter so idle loop does not set hEventAllDone for
  2176. // a request he has not yet started.
  2177. npMCI->hWaiter = hWaiter;
  2178. }
  2179. // else the request has already been dealt with
  2180. }
  2181. // did the winproc have any requests
  2182. if (WaitForSingleObject(npMCI->heWinProcRequest, 0) == WAIT_OBJECT_0) {
  2183. //
  2184. // We have a request from the window thread. Go process it
  2185. //
  2186. OnTask_WinProcRequests(npMCI, TRUE);
  2187. }
  2188. }
  2189. /***************************************************************************
  2190. *
  2191. * @doc INTERNAL MCIAVI
  2192. *
  2193. * @api DWORD | CheckIfActive | check to see if we are the active movie
  2194. *
  2195. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data block.
  2196. *
  2197. * @rdesc 0 means OK, otherwise mci error
  2198. *
  2199. ***************************************************************************/
  2200. void CheckIfActive(NPMCIGRAPHIC npMCI)
  2201. {
  2202. BOOL fActive;
  2203. HWND hwndA;
  2204. if (!IsTask(npMCI->hTask)) return;
  2205. //
  2206. // are we the foreground window?
  2207. //
  2208. // ??? should the value of <npMCI->fForceBackground> matter?
  2209. //
  2210. // IMPORTANT: This does NOT work under NT. The best that can
  2211. // be done is to check GetForegroundWindow
  2212. #ifndef _WIN32
  2213. hwndA = GetActiveWindow();
  2214. fActive = (hwndA == npMCI->hwndPlayback) ||
  2215. (GetFocus() == npMCI->hwndPlayback) ||
  2216. (IsWindow(hwndA) && IsChild(hwndA, npMCI->hwndPlayback) && !npMCI->fForceBackground);
  2217. #else
  2218. hwndA = GetForegroundWindow();
  2219. fActive = (hwndA == npMCI->hwndPlayback) ||
  2220. (IsWindow(hwndA) && IsChild(hwndA, npMCI->hwndPlayback) && !npMCI->fForceBackground);
  2221. #endif
  2222. DeviceSetActive(npMCI, fActive);
  2223. }