Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

561 lines
13 KiB

  1. #include "ctlspriv.h"
  2. #include "rlefile.h"
  3. #define RectWid(_rc) ((_rc).right-(_rc).left)
  4. #define RectHgt(_rc) ((_rc).bottom-(_rc).top)
  5. typedef struct
  6. {
  7. HWND hwnd; // my window
  8. int id; // my id
  9. HWND hwndP; // my owner (get notify messages)
  10. DWORD style;
  11. BOOL fFirstPaint; // TRUE until first paint.
  12. RLEFILE *prle;
  13. CRITICAL_SECTION crit;
  14. RECT rc;
  15. int NumFrames;
  16. int Rate;
  17. int iFrame;
  18. int PlayCount;
  19. int PlayFrom;
  20. int PlayTo;
  21. HANDLE PaintThread;
  22. HANDLE hStopEvent;
  23. } ANIMATE;
  24. #define Enter(p) EnterCriticalSection(&p->crit)
  25. #define Leave(p) LeaveCriticalSection(&p->crit)
  26. #define OPEN_WINDOW_TEXT 42
  27. // Threading is broken with the new "Transparent" animations. If we decide to reenable
  28. // this, we need to figure out how to get the bits without using sendmessage.
  29. #define Ani_UseThread(p) (FALSE)
  30. LRESULT CALLBACK AnimateWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
  31. BOOL HandleOpen(ANIMATE *p, HINSTANCE hInst, LPCTSTR pszName, UINT flags);
  32. BOOL HandleStop(ANIMATE *p);
  33. BOOL HandlePlay(ANIMATE *p, int from, int to, int count);
  34. void HandlePaint(ANIMATE *p, HDC hdc);
  35. int HandleTick(ANIMATE *p);
  36. BOOL InitAnimateClass(HINSTANCE hInstance)
  37. {
  38. WNDCLASS wc;
  39. wc.lpfnWndProc = AnimateWndProc;
  40. wc.lpszClassName = ANIMATE_CLASS;
  41. wc.style = CS_DBLCLKS | CS_GLOBALCLASS;
  42. wc.cbClsExtra = 0;
  43. wc.cbWndExtra = sizeof(LPVOID);
  44. wc.hInstance = hInstance; // use DLL instance if in DLL
  45. wc.hIcon = NULL;
  46. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  47. wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
  48. wc.lpszMenuName = NULL;
  49. if (!RegisterClass(&wc) && !GetClassInfo(hInstance, ANIMATE_CLASS, &wc))
  50. return FALSE;
  51. return TRUE;
  52. }
  53. BOOL HandleOpen(ANIMATE *p, HINSTANCE hInst, LPCTSTR pszName, UINT flags)
  54. {
  55. TCHAR ach[MAX_PATH];
  56. //
  57. // use window text as file name
  58. //
  59. if (flags == OPEN_WINDOW_TEXT)
  60. {
  61. GetWindowText(p->hwnd, ach, ARRAYSIZE(ach));
  62. pszName = ach;
  63. }
  64. if (hInst == NULL)
  65. hInst = (HINSTANCE)GetWindowLongPtr(p->hwnd, GWLP_HINSTANCE);
  66. HandleStop(p); // stop a play first
  67. if (p->prle)
  68. {
  69. RleFile_Free(p->prle);
  70. p->prle = NULL;
  71. }
  72. p->iFrame = 0;
  73. p->NumFrames = 0;
  74. if (pszName == NULL || (!IS_INTRESOURCE(pszName) && *pszName == 0))
  75. return FALSE;
  76. //
  77. // now open the file/resource we got.
  78. //
  79. p->prle = RleFile_New();
  80. if (p->prle == NULL)
  81. return FALSE;
  82. if (!RleFile_OpenFromResource(p->prle, hInst, pszName, TEXT("AVI")) &&
  83. !RleFile_OpenFromFile(p->prle, pszName))
  84. {
  85. RleFile_Free(p->prle);
  86. p->prle = NULL;
  87. return FALSE;
  88. }
  89. else
  90. {
  91. p->NumFrames = RleFile_NumFrames(p->prle);
  92. p->Rate = (int)RleFile_Rate(p->prle);
  93. SetRect(&p->rc, 0, 0, RleFile_Width(p->prle), RleFile_Height(p->prle));
  94. }
  95. //
  96. // handle a transparent color
  97. //
  98. if ((p->style & ACS_TRANSPARENT) && p->hwndP)
  99. {
  100. HDC hdc;
  101. HDC hdcM;
  102. HBITMAP hbm;
  103. COLORREF rgbS = {0};
  104. COLORREF rgbD = {0};
  105. hdc = GetDC(p->hwnd);
  106. //
  107. // create a bitmap and draw image into it.
  108. // get upper left pixel and make that transparent.
  109. //
  110. hdcM= CreateCompatibleDC(hdc);
  111. if (hdcM)
  112. {
  113. hbm = CreateCompatibleBitmap(hdc, 1, 1);
  114. if (hbm)
  115. {
  116. HBITMAP hbmO = SelectObject(hdcM, hbm);
  117. RleFile_Paint( p->prle, hdcM, p->iFrame, p->rc.left, p->rc.top );
  118. rgbS = GetPixel(hdcM, 0, 0);
  119. SelectObject(hdcM, hbmO);
  120. DeleteObject(hbm);
  121. }
  122. DeleteDC(hdcM);
  123. }
  124. SendMessage(p->hwndP, GET_WM_CTLCOLOR_MSG(CTLCOLOR_STATIC),
  125. GET_WM_CTLCOLOR_MPS(hdc, p->hwnd, CTLCOLOR_STATIC));
  126. rgbD = GetBkColor(hdc);
  127. ReleaseDC(p->hwnd, hdc);
  128. //
  129. // now replace the color
  130. //
  131. RleFile_ChangeColor(p->prle, rgbS, rgbD);
  132. }
  133. //
  134. // ok it worked, resize window.
  135. //
  136. if (p->style & ACS_CENTER)
  137. {
  138. RECT rc;
  139. GetClientRect(p->hwnd, &rc);
  140. OffsetRect(&p->rc, (rc.right-p->rc.right)/2,(rc.bottom-p->rc.bottom)/2);
  141. }
  142. else
  143. {
  144. RECT rc;
  145. rc = p->rc;
  146. AdjustWindowRectEx(&rc, GetWindowStyle(p->hwnd), FALSE, GetWindowExStyle(p->hwnd));
  147. SetWindowPos(p->hwnd, NULL, 0, 0, RectWid(rc), RectHgt(rc),
  148. SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
  149. }
  150. if (p->style & ACS_AUTOPLAY)
  151. {
  152. PostMessage(p->hwnd, ACM_PLAY, (UINT_PTR)-1, MAKELONG(0, -1));
  153. }
  154. else
  155. {
  156. InvalidateRect(p->hwnd, NULL, TRUE);
  157. }
  158. return TRUE;
  159. }
  160. void DoNotify(ANIMATE *p, int cmd)
  161. {
  162. if (p->hwndP)
  163. PostMessage(p->hwndP, WM_COMMAND, GET_WM_COMMAND_MPS(p->id, p->hwnd, cmd));
  164. }
  165. BOOL HandleStop(ANIMATE *p)
  166. {
  167. if (p == NULL || !p->PaintThread)
  168. return FALSE;
  169. if (Ani_UseThread(p))
  170. {
  171. // set thread up to terminate between frames
  172. Enter( p );
  173. p->PlayCount = 0;
  174. Leave( p );
  175. if (p->hStopEvent)
  176. SetEvent(p->hStopEvent);
  177. WaitForSingleObject(p->PaintThread, INFINITE);
  178. CloseHandle(p->PaintThread);
  179. // PORT QSY
  180. p->PaintThread = NULL;
  181. if (p->hStopEvent)
  182. CloseHandle(p->hStopEvent);
  183. p->hStopEvent = NULL;
  184. }
  185. else
  186. {
  187. KillTimer(p->hwnd, HandleToUlong(p->PaintThread)); // really was a UINT
  188. p->PaintThread = 0;
  189. DoNotify(p, ACN_STOP);
  190. }
  191. return TRUE;
  192. }
  193. int PlayThread(ANIMATE *p)
  194. {
  195. int result;
  196. DoNotify(p, ACN_START);
  197. while (result = HandleTick(p))
  198. {
  199. // Sleep for a bit (4 seconds) longer if we are hidden
  200. //
  201. // Old code here slept, which can block the UI thread
  202. // if the app tries to stop/shutdown/change the animation
  203. // right near the beginning of the sleep.
  204. // Sleep((result < 0 ? p->Rate+4000 : p->Rate));
  205. // Do a timed wait for the stop event instead
  206. //
  207. if (p->hStopEvent)
  208. WaitForSingleObject(p->hStopEvent, (result < 0 ? p->Rate+4000 : p->Rate));
  209. else
  210. Sleep((result < 0 ? p->Rate+4000 : p->Rate));
  211. }
  212. DoNotify(p, ACN_STOP);
  213. return 0;
  214. }
  215. BOOL HandlePlay(ANIMATE *p, int from, int to, int count)
  216. {
  217. if (p == NULL || p->prle == NULL)
  218. return FALSE;
  219. HandleStop(p);
  220. if (from >= p->NumFrames)
  221. from = p->NumFrames-1;
  222. if (to == -1)
  223. to = p->NumFrames-1;
  224. if (to < 0)
  225. to = 0;
  226. if (to >= p->NumFrames)
  227. to = p->NumFrames-1;
  228. p->PlayCount = count;
  229. p->PlayTo = to;
  230. if (from >= 0)
  231. {
  232. p->iFrame = from;
  233. p->PlayFrom = from;
  234. }
  235. else
  236. from = p->PlayFrom;
  237. if ( (from == to) || !count )
  238. {
  239. InvalidateRect(p->hwnd, NULL, TRUE);
  240. return TRUE;
  241. }
  242. InvalidateRect(p->hwnd, NULL, FALSE);
  243. UpdateWindow(p->hwnd);
  244. if (Ani_UseThread(p))
  245. {
  246. DWORD dw;
  247. p->hStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  248. p->PaintThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PlayThread, (void*)p, 0, &dw);
  249. }
  250. else
  251. {
  252. DoNotify(p, ACN_START);
  253. p->PaintThread = (HANDLE)SetTimer(p->hwnd, 42, (UINT)p->Rate, NULL);
  254. }
  255. return TRUE;
  256. }
  257. void HandleFirstPaint(ANIMATE *p)
  258. {
  259. if (p->fFirstPaint)
  260. {
  261. p->fFirstPaint = FALSE;
  262. if (p->NumFrames == 0 &&
  263. (p->style & WS_CHILD))
  264. {
  265. HandleOpen(p, NULL, NULL, OPEN_WINDOW_TEXT);
  266. }
  267. }
  268. }
  269. void HandlePaint(ANIMATE *p, HDC hdc)
  270. {
  271. if( p && p->prle )
  272. {
  273. HDC h;
  274. HBRUSH hbr;
  275. RECT rc;
  276. CCDBUFFER db;
  277. GetClientRect(p->hwnd, &rc);
  278. h = CCBeginDoubleBuffer(hdc, &rc, &db);
  279. hbr = (HBRUSH)SendMessage(p->hwndP, GET_WM_CTLCOLOR_MSG(CTLCOLOR_STATIC),
  280. GET_WM_CTLCOLOR_MPS(hdc, p->hwnd, CTLCOLOR_STATIC));
  281. FillRect(h, &rc, hbr);
  282. Enter( p );
  283. RleFile_Paint( p->prle, h, p->iFrame, p->rc.left, p->rc.top );
  284. Leave( p );
  285. CCEndDoubleBuffer(&db);
  286. }
  287. }
  288. void HandlePrint(ANIMATE *p, HDC hdc)
  289. {
  290. HandleFirstPaint(p);
  291. HandlePaint(p, hdc);
  292. }
  293. int HandleTick(ANIMATE *p)
  294. // - if something to do but we are hidden
  295. // returns 0 if nothing left
  296. // + if something to do
  297. {
  298. int result = 0;
  299. if( p && p->prle )
  300. {
  301. HDC hdc;
  302. RECT dummy;
  303. Enter( p );
  304. hdc = GetDC( p->hwnd );
  305. if( GetClipBox( hdc, &dummy ) != NULLREGION )
  306. {
  307. HandlePaint( p, hdc );
  308. if( p->iFrame >= p->PlayTo )
  309. {
  310. if( p->PlayCount > 0 )
  311. p->PlayCount--;
  312. if( p->PlayCount != 0 )
  313. p->iFrame = p->PlayFrom;
  314. }
  315. else
  316. p->iFrame++;
  317. // Something to do? and visible, return + value
  318. result = ( p->PlayCount != 0 );
  319. }
  320. else
  321. {
  322. // Something to do? but hidden, so return - value
  323. p->iFrame = p->PlayFrom;
  324. result = -( p->PlayCount != 0 );
  325. }
  326. ReleaseDC( p->hwnd, hdc );
  327. Leave( p );
  328. }
  329. return result;
  330. }
  331. void Ani_OnStyleChanged(ANIMATE* p, WPARAM gwl, STYLESTRUCT *pinfo)
  332. {
  333. if (gwl == GWL_STYLE)
  334. {
  335. p->style = pinfo->styleNew;
  336. }
  337. }
  338. LRESULT CALLBACK AnimateWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  339. {
  340. ANIMATE *p = (ANIMATE *)GetWindowPtr(hwnd, 0);
  341. HDC hdc;
  342. PAINTSTRUCT ps;
  343. // First, the messages that can handle p == NULL.
  344. // All these handlers must end with a "return" or a "goto DoDefault".
  345. switch (msg)
  346. {
  347. case WM_NCCREATE:
  348. #define lpcs ((LPCREATESTRUCT)lParam)
  349. p = (ANIMATE *)LocalAlloc(LPTR, sizeof(ANIMATE));
  350. if (!p)
  351. return 0; // WM_NCCREATE failure is 0
  352. // note, zero init memory from above
  353. p->hwnd = hwnd;
  354. p->hwndP = lpcs->hwndParent;
  355. p->id = PtrToUlong(lpcs->hMenu); // really was an int
  356. p->fFirstPaint = TRUE;
  357. p->style = lpcs->style;
  358. // Must do this before SetWindowBits because that will recursively
  359. // cause us to receive WM_STYLECHANGED and possibly even WM_SIZE
  360. // messages.
  361. InitializeCriticalSection(&p->crit);
  362. SetWindowPtr(hwnd, 0, p);
  363. //
  364. // UnMirror the control, if it is mirrored. We shouldn't mirror
  365. // a movie! [samera]
  366. //
  367. SetWindowBits(hwnd, GWL_EXSTYLE, RTL_MIRRORED_WINDOW, 0);
  368. goto DoDefault;
  369. case WM_CLOSE:
  370. Animate_Stop(hwnd);
  371. goto DoDefault;
  372. case WM_NCHITTEST:
  373. return HTTRANSPARENT;
  374. case WM_GETOBJECT:
  375. if (lParam == OBJID_QUERYCLASSNAMEIDX)
  376. return MSAA_CLASSNAMEIDX_ANIMATE;
  377. goto DoDefault;
  378. }
  379. // Okay, now the messages that cannot handle p == NULL.
  380. // We check p == NULL once and for all.
  381. if (!p)
  382. goto DoDefault;
  383. switch (msg)
  384. {
  385. case WM_DESTROY:
  386. Animate_Close(hwnd);
  387. DeleteCriticalSection(&p->crit);
  388. LocalFree((HLOCAL)p);
  389. SetWindowPtr(hwnd, 0, 0);
  390. break;
  391. case WM_ERASEBKGND:
  392. return(1);
  393. case WM_PAINT:
  394. HandleFirstPaint(p);
  395. hdc = BeginPaint(hwnd, &ps);
  396. HandlePaint(p, hdc);
  397. EndPaint(hwnd, &ps);
  398. return 0;
  399. case WM_PRINTCLIENT:
  400. HandlePrint(p, (HDC)wParam);
  401. return 0;
  402. case WM_STYLECHANGED:
  403. Ani_OnStyleChanged(p, wParam, (LPSTYLESTRUCT)lParam);
  404. return 0L;
  405. case WM_SIZE:
  406. if (p->style & ACS_CENTER)
  407. {
  408. OffsetRect(&p->rc, (LOWORD(lParam)-RectWid(p->rc))/2-p->rc.left,
  409. (HIWORD(lParam)-RectHgt(p->rc))/2-p->rc.top);
  410. InvalidateRect(hwnd, NULL, TRUE);
  411. }
  412. break;
  413. case WM_TIMER:
  414. {
  415. int result = HandleTick(p);
  416. if (!result)
  417. {
  418. HandleStop(p);
  419. }
  420. else if (result < 0)
  421. {
  422. p->PaintThread = (HANDLE)SetTimer(p->hwnd, 42, (UINT)p->Rate+4000, NULL);
  423. }
  424. else
  425. {
  426. p->PaintThread = (HANDLE)SetTimer(p->hwnd, 42, (UINT)p->Rate, NULL);
  427. }
  428. }
  429. break;
  430. case ACM_OPENA:
  431. {
  432. WCHAR szFileNameW[MAX_PATH];
  433. LPTSTR lpFileName = szFileNameW;
  434. if (!IS_INTRESOURCE(lParam))
  435. {
  436. MultiByteToWideChar (CP_ACP, 0, (LPCSTR)lParam, -1, szFileNameW, MAX_PATH);
  437. }
  438. else
  439. {
  440. lpFileName = (LPTSTR) lParam;
  441. }
  442. return HandleOpen(p, (HINSTANCE)wParam, lpFileName, 0);
  443. }
  444. case ACM_OPEN:
  445. return HandleOpen(p, (HINSTANCE)wParam, (LPCTSTR)lParam, 0);
  446. case ACM_STOP:
  447. return HandleStop(p);
  448. case ACM_PLAY:
  449. return HandlePlay(p, (int)(SHORT)LOWORD(lParam), (int)(SHORT)HIWORD(lParam), (int)wParam);
  450. }
  451. DoDefault:
  452. return DefWindowProc(hwnd, msg, wParam, lParam);
  453. }