TB2Anim.pas 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. unit TB2Anim;
  2. {
  3. Toolbar2000
  4. Copyright (C) 1998-2005 by Jordan Russell
  5. All rights reserved.
  6. The contents of this file are subject to the "Toolbar2000 License"; you may
  7. not use or distribute this file except in compliance with the
  8. "Toolbar2000 License". A copy of the "Toolbar2000 License" may be found in
  9. TB2k-LICENSE.txt or at:
  10. http://www.jrsoftware.org/files/tb2k/TB2k-LICENSE.txt
  11. Alternatively, the contents of this file may be used under the terms of the
  12. GNU General Public License (the "GPL"), in which case the provisions of the
  13. GPL are applicable instead of those in the "Toolbar2000 License". A copy of
  14. the GPL may be found in GPL-LICENSE.txt or at:
  15. http://www.jrsoftware.org/files/tb2k/GPL-LICENSE.txt
  16. If you wish to allow use of your version of this file only under the terms of
  17. the GPL and not to allow others to use your version of this file under the
  18. "Toolbar2000 License", indicate your decision by deleting the provisions
  19. above and replace them with the notice and other provisions required by the
  20. GPL. If you do not delete the provisions above, a recipient may use your
  21. version of this file under either the "Toolbar2000 License" or the GPL.
  22. $jrsoftware: tb2k/Source/TB2Anim.pas,v 1.11 2005/02/16 08:15:58 jr Exp $
  23. }
  24. interface
  25. {$I TB2Ver.inc}
  26. {$Q-}
  27. uses
  28. Windows, Messages, SysUtils, Classes;
  29. const
  30. WM_TB2K_STEPANIMATION = WM_USER + $555;
  31. WM_TB2K_ANIMATIONENDED = WM_USER + $556;
  32. type
  33. TTBAnimationDirection = set of (tbadLeft, tbadRight, tbadDown, tbadUp);
  34. procedure TBStartAnimation(const AWnd: HWND; const ABlend: Boolean;
  35. const ADirection: TTBAnimationDirection);
  36. procedure TBStepAnimation(const Msg: TMessage);
  37. procedure TBEndAnimation(const Wnd: HWND);
  38. implementation
  39. { Notes to self:
  40. - It originally had the NOMIRRORBITMAP flag on the BitBlt calls, because
  41. Windows 2000's AnimateWindow function has it. But it had to be removed
  42. because on Windows 98 with the Standard VGA or VMware video driver, it
  43. caused no bits to be blitted, even though Windows 98 is supposed to
  44. support NOMIRRORBITMAP according to the documentation. I don't think it's
  45. necessary anyway.
  46. }
  47. const
  48. DCX_USESTYLE = $10000;
  49. WS_EX_LAYERED = $80000;
  50. NOMIRRORBITMAP = $80000000;
  51. ULW_ALPHA = 2;
  52. type
  53. PAnimateThreadFuncData = ^TAnimateThreadFuncData;
  54. TAnimateThreadFuncData = record
  55. SequenceID: Integer;
  56. Wnd: HWND;
  57. Time: Integer;
  58. Blending: Boolean;
  59. CurStep: Integer;
  60. DC, BmpDC: HDC;
  61. Bmp: HBITMAP;
  62. ScreenClientRect: TRect;
  63. Size: TPoint;
  64. LastPos: TPoint;
  65. Direction: TTBAnimationDirection;
  66. AnimateThreadAbort: BOOL;
  67. AnimationEnded: BOOL;
  68. StepMessagePending: BOOL;
  69. end;
  70. var
  71. UpdateLayeredWindowProc: function(Handle: THandle; hdcDest: HDC;
  72. pptDst: PPoint; _psize: PSize; hdcSrc: HDC; pptSrc: PPoint;
  73. crKey: COLORREF; pblend: PBLENDFUNCTION; dwFlags: DWORD): BOOL; stdcall;
  74. function AnimateThreadFunc(Parameter: Pointer): Integer;
  75. var
  76. StartTime, FrameStartTime, NextFrameStartTime: DWORD;
  77. StartStep, ElapsedTime, I: Integer;
  78. P: TPoint;
  79. begin
  80. Result := 0;
  81. StartTime := GetTickCount;
  82. FrameStartTime := StartTime;
  83. with PAnimateThreadFuncData(Parameter)^ do begin
  84. StartStep := CurStep;
  85. while not AnimateThreadAbort do begin
  86. ElapsedTime := FrameStartTime - StartTime;
  87. if (ElapsedTime < 0) or (ElapsedTime >= Time) then
  88. Break;
  89. I := StartStep + ((255 * ElapsedTime) div Time);
  90. if (I < 0) or (I >= 255) then
  91. Break;
  92. GetCursorPos(P);
  93. if (P.X <> LastPos.X) or (P.Y <> LastPos.Y) then begin
  94. if PtInRect(ScreenClientRect, P) then
  95. Break;
  96. LastPos := P;
  97. end;
  98. if I > CurStep then begin
  99. CurStep := I;
  100. if InterlockedExchange(Integer(StepMessagePending), 1) = 0 then
  101. SendNotifyMessage(Wnd, WM_TB2K_STEPANIMATION, 0, SequenceID);
  102. end;
  103. { Wait until the timer has ticked at least 10 msec }
  104. NextFrameStartTime := GetTickCount;
  105. while NextFrameStartTime - FrameStartTime < 10 do begin
  106. { We don't know the rate of the system timer, so check every 5 msec
  107. to see if it's incremented }
  108. Sleep(5);
  109. NextFrameStartTime := GetTickCount;
  110. end;
  111. FrameStartTime := NextFrameStartTime;
  112. end;
  113. AnimationEnded := True;
  114. SendNotifyMessage(Wnd, WM_TB2K_STEPANIMATION, 0, SequenceID);
  115. end;
  116. end;
  117. threadvar
  118. AnimateThreadHandle: THandle;
  119. AnimateData: TAnimateThreadFuncData;
  120. AnimationSequenceID: Integer;
  121. procedure FinalizeAnimation;
  122. begin
  123. with PAnimateThreadFuncData(@AnimateData)^ do begin
  124. if Blending then
  125. SetWindowLong(Wnd, GWL_EXSTYLE,
  126. GetWindowLong(Wnd, GWL_EXSTYLE) and not WS_EX_LAYERED)
  127. else
  128. SetWindowRgn(Wnd, 0, False);
  129. BitBlt(DC, 0, 0, Size.X, Size.Y, BmpDC, 0, 0, SRCCOPY);
  130. DeleteDC(BmpDC);
  131. DeleteObject(Bmp);
  132. ReleaseDC(Wnd, DC);
  133. SendNotifyMessage(Wnd, WM_TB2K_ANIMATIONENDED, 0, 0);
  134. end;
  135. end;
  136. procedure TBEndAnimation(const Wnd: HWND);
  137. begin
  138. if (AnimateThreadHandle <> 0) and
  139. ((Wnd = 0) or (AnimateData.Wnd = Wnd)) then begin
  140. AnimateData.AnimateThreadAbort := True;
  141. WaitForSingleObject(AnimateThreadHandle, INFINITE);
  142. CloseHandle(AnimateThreadHandle);
  143. AnimateThreadHandle := 0;
  144. FinalizeAnimation;
  145. end;
  146. end;
  147. procedure TBStartAnimation(const AWnd: HWND; const ABlend: Boolean;
  148. const ADirection: TTBAnimationDirection);
  149. var
  150. ZeroPt: TPoint;
  151. R: TRect;
  152. ThreadID: DWORD;
  153. Blend: TBlendFunction;
  154. Rgn: HRGN;
  155. begin
  156. TBEndAnimation(0);
  157. ZeroPt.X := 0;
  158. ZeroPt.Y := 0;
  159. Inc(AnimationSequenceID);
  160. FillChar(AnimateData, SizeOf(AnimateData), 0);
  161. { Note: The pointer cast avoids GetTls calls for every field access }
  162. with PAnimateThreadFuncData(@AnimateData)^ do begin
  163. SequenceID := AnimationSequenceID;
  164. Wnd := AWnd;
  165. Blending := ABlend and Assigned(UpdateLayeredWindowProc);
  166. Direction := ADirection;
  167. GetCursorPos(LastPos);
  168. GetClientRect(Wnd, ScreenClientRect);
  169. MapWindowPoints(Wnd, 0, ScreenClientRect, 2);
  170. GetWindowRect(Wnd, R);
  171. DC := GetDCEx(Wnd, 0, DCX_WINDOW or DCX_CACHE {or DCX_USESTYLE ?});
  172. Size.X := R.Right - R.Left;
  173. Size.Y := R.Bottom - R.Top;
  174. Bmp := CreateCompatibleBitmap(DC, Size.X, Size.Y {or $01000000 ?});
  175. BmpDC := CreateCompatibleDC(DC);
  176. // AnimateWindow calls SetLayout, but I'm not sure that we need to.
  177. //if Assigned(SetLayoutProc) then
  178. // SetLayoutProc(BmpDC, 0);
  179. SelectObject(BmpDC, Bmp);
  180. //SetBoundsRect(BmpDC, nil, DCB_RESET or DCB_ENABLE);
  181. SendMessage(Wnd, WM_PRINT, WPARAM(BmpDC), PRF_NONCLIENT or PRF_CLIENT or
  182. PRF_ERASEBKGND or PRF_CHILDREN);
  183. //GetBoundsRect
  184. if Blending then begin
  185. SetWindowLong(Wnd, GWL_EXSTYLE, GetWindowLong(Wnd, GWL_EXSTYLE) or WS_EX_LAYERED);
  186. Time := 175; { actually more like ~147 because CurStep starts at 40 }
  187. CurStep := 40;
  188. Longint(Blend) := 0;
  189. Blend.BlendOp := AC_SRC_OVER;
  190. Blend.SourceConstantAlpha := CurStep;
  191. UpdateLayeredWindowProc(Wnd, 0, @R.TopLeft, @Size, BmpDC, @ZeroPt, 0,
  192. @Blend, ULW_ALPHA);
  193. end
  194. else begin
  195. Time := 150;
  196. CurStep := 0;
  197. Rgn := CreateRectRgn(0, 0, 0, 0);
  198. if not BOOL(SetWindowRgn(Wnd, Rgn, False)) then
  199. DeleteObject(Rgn); { just in case }
  200. end;
  201. { These are the same flags AnimateWindow uses. SWP_ASYNCWINDOWPOS is
  202. needed or else it doesn't "save bits" properly.
  203. Note: SWP_ASYNCWINDOWPOS seems to have no effect on Windows 95 & NT 4.0,
  204. so bits behind the window are not saved & restored correctly. }
  205. SetWindowPos(Wnd, 0, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOMOVE or
  206. SWP_NOZORDER or SWP_NOACTIVATE or SWP_SHOWWINDOW or SWP_NOREDRAW or
  207. SWP_NOOWNERZORDER or SWP_ASYNCWINDOWPOS);
  208. end;
  209. AnimateThreadHandle := BeginThread(nil, 0, AnimateThreadFunc, @AnimateData,
  210. CREATE_SUSPENDED, ThreadID);
  211. if AnimateThreadHandle = 0 then begin
  212. { just in case... }
  213. FinalizeAnimation;
  214. Exit;
  215. end;
  216. ResumeThread(AnimateThreadHandle);
  217. end;
  218. procedure TBStepAnimation(const Msg: TMessage);
  219. var
  220. Rgn: HRGN;
  221. Blend: TBlendFunction;
  222. X, Y: Integer;
  223. begin
  224. if Msg.LParam <> AnimationSequenceID then
  225. { ignore messages dangling in the queue from aborted animation sequences }
  226. Exit;
  227. with PAnimateThreadFuncData(@AnimateData)^ do begin
  228. if not AnimationEnded then begin
  229. if Blending then begin
  230. Longint(Blend) := 0;
  231. Blend.BlendOp := AC_SRC_OVER;
  232. Blend.SourceConstantAlpha := AnimateData.CurStep;
  233. UpdateLayeredWindowProc(Wnd, 0, nil, nil, 0, nil, 0, @Blend, ULW_ALPHA);
  234. end
  235. else begin
  236. if tbadDown in Direction then
  237. Y := MulDiv(Size.Y, AnimateData.CurStep, 255) - Size.Y
  238. else if tbadUp in Direction then
  239. Y := Size.Y - MulDiv(Size.Y, AnimateData.CurStep, 255)
  240. else
  241. Y := 0;
  242. if tbadRight in Direction then
  243. X := MulDiv(Size.X, AnimateData.CurStep, 255) - Size.X
  244. else if tbadLeft in Direction then
  245. X := Size.X - MulDiv(Size.X, AnimateData.CurStep, 255)
  246. else
  247. X := 0;
  248. Rgn := CreateRectRgn(X, Y, X + Size.X, Y + Size.Y);
  249. if not BOOL(SetWindowRgn(Wnd, Rgn, False)) then
  250. DeleteObject(Rgn); { just in case }
  251. BitBlt(DC, X, Y, Size.X, Size.Y, BmpDC, 0, 0, SRCCOPY);
  252. end;
  253. end
  254. else
  255. TBEndAnimation(Wnd);
  256. StepMessagePending := False;
  257. end;
  258. end;
  259. initialization
  260. UpdateLayeredWindowProc := GetProcAddress(GetModuleHandle(user32),
  261. 'UpdateLayeredWindow');
  262. finalization
  263. TBEndAnimation(0);
  264. end.