GLS.ScreenSaver.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. //
  2. // The graphics engine GLScene
  3. //
  4. unit GLS.ScreenSaver;
  5. (* Component for making screen-savers an easy task *)
  6. interface
  7. {$I Stage.Defines.inc}
  8. uses
  9. Winapi.Windows,
  10. Winapi.Messages,
  11. System.Classes,
  12. System.SysUtils,
  13. System.Win.Registry,
  14. VCL.Dialogs,
  15. VCL.Controls,
  16. VCL.Forms,
  17. VCL.Extctrls;
  18. type
  19. (* Options d'automatisation du screen-saver.
  20. ssoAutoAdjustFormProperties : all relevant properties of main form
  21. will be auto-adjusted (form style, border style, form size and for
  22. preview, ParentWindow).
  23. ssoAutoHookKeyboardEvents : hooks to main form's OnKeyPress and closes
  24. screen saver when a key is pressed (form's KeyPreview is also set to True)
  25. ssoAutoHookMouseEvents : hooks to main form's OnMouseMove and closes
  26. screen saver when mouse is moved (you mays have to handle other mouse
  27. move events manually if you have placed components on the form)
  28. ssoEnhancedMouseMoveDetection : gets the mouse position every half-second
  29. and closes the saver if position changed (uses GetCursorPos and a TTimer) *)
  30. TScreenSaverOption = (ssoAutoAdjustFormProperties, ssoAutoHookKeyboardEvents,
  31. ssoAutoHookMouseEvents, ssoEnhancedMouseMoveDetection);
  32. TScreenSaverOptions = set of TScreenSaverOption;
  33. const
  34. cDefaultScreenSaverOptions = [ssoAutoAdjustFormProperties,
  35. ssoAutoHookKeyboardEvents, ssoEnhancedMouseMoveDetection];
  36. type
  37. (* This event is fired when screen saver should start in preview mode.
  38. The passed hwnd is that of the small preview window in Windows Display
  39. Properties (or any other screen-saver previewing utility, so don't
  40. assume width/height is constant/universal or whatever). *)
  41. TScreenSaverPreviewEvent = procedure(Sender: TObject; previewHwnd: HWND)
  42. of object;
  43. (* Drop this component on your main form to make it a screensaver.
  44. You'll also need to change the extension from ".exe" to ".scr" (in the
  45. project options / application tab).
  46. How this component works :
  47. At design-time, the only event you may want to hook is
  48. OnPropertiesRequested (to diplay your screen-saver's config dialog,
  49. if you don't have one, at least fill in the AboutString property
  50. and it will be used in a ShowMessage)
  51. At run-time, once its props are loaded, this component will parse the
  52. command line and trigger relevant events
  53. Basicly, you only need to care about drawing in your main form's
  54. client area (in a resolution/size independant way if possible)
  55. There is no real difference between execution and preview modes, except
  56. for the events fired... and the size of the form :). *)
  57. TGLScreenSaver = class(TComponent)
  58. private
  59. mouseEventsToIgnore: Integer;
  60. FHonourWindowsPassword: Boolean;
  61. FOptions: TScreenSaverOptions;
  62. FOnPropertiesRequested: TNotifyEvent;
  63. FOnExecute: TNotifyEvent;
  64. FOnPreview: TScreenSaverPreviewEvent;
  65. FOnCloseQuery: TCloseQueryEvent;
  66. FAboutString: String;
  67. FInPreviewMode: Boolean;
  68. mouseTimer: TTimer; // alocated only if necessary
  69. lastMousePosition: TPoint;
  70. FMutex: THandle;
  71. protected
  72. procedure Loaded; override;
  73. procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
  74. procedure FormKeyPress(Sender: TObject; var Key: Char);
  75. procedure OnMouseTimer(Sender: TObject);
  76. procedure ConfigureSaver;
  77. procedure PreviewSaver;
  78. procedure ExecuteSaver;
  79. public
  80. constructor Create(AOwner: TComponent); override;
  81. destructor Destroy; override;
  82. (* Invokes the standard Windows dialog to set the password.
  83. May be invoked from your Properties/Configuration dialog. *)
  84. procedure SetPassword;
  85. (* Properly handles request to close the main window.
  86. Returns True if the Close request wasn't canceled (by event or
  87. password fail) and will actually happen.
  88. Use this if you implemented specific screen-saver exiting in your
  89. main form.
  90. It first triggers the OnCloseQuery, where the close request can be
  91. canceled, if this passed, the password is checked if there is any,
  92. then sends a WM_CLOSE to the saver windows. *)
  93. function CloseSaver: Boolean;
  94. (* True if the screen-save is in preview mode.
  95. Valid only when the TScreenSaver has completed loading. *)
  96. property InPreviewMode: Boolean read FInPreviewMode;
  97. published
  98. property Options: TScreenSaverOptions read FOptions write FOptions
  99. default cDefaultScreenSaverOptions;
  100. (* If True, windows screen-saver password is checked before closing.
  101. You may be wanting to set this prop to false if you're using your
  102. own password scheme or do not want any password to be set. *)
  103. property HonourWindowsPassword: Boolean read FHonourWindowsPassword
  104. write FHonourWindowsPassword default True;
  105. { This string is displayed if OnPropertiesRequested is not used.
  106. You may use it as a quick "AboutBox". }
  107. property AboutString: String read FAboutString write FAboutString;
  108. (* Display the properties dialog when this event is triggered.
  109. This event may be called before Delphi's form auto-creation is complete,
  110. and should not rely on auto-created dialogs/forms but create what
  111. needs be. *)
  112. property OnPropertiesRequested: TNotifyEvent read FOnPropertiesRequested
  113. write FOnPropertiesRequested;
  114. // Fired when the saver should start executing, after form props are adjusted.
  115. property OnExecute: TNotifyEvent read FOnExecute write FOnExecute;
  116. // Fired when preview is requested, after form props are adjusted.
  117. property OnPreview: TScreenSaverPreviewEvent read FOnPreview
  118. write FOnPreview;
  119. (* Fired when screen-saver execution should close.
  120. It is invoked before querying for password (if there is a password). *)
  121. property OnCloseQuery: TCloseQueryEvent read FOnCloseQuery
  122. write FOnCloseQuery;
  123. end;
  124. (* Invokes the standard Windows dialog to set the password.
  125. May be invoked from your Properties/Configuration dialog. *)
  126. procedure SetScreenSaverPassword;
  127. // ---------------------------------------------------------------------
  128. implementation
  129. // ---------------------------------------------------------------------
  130. // Returns system path and makes sure there is a trailing '\'.
  131. function GetSystemDirectory: String;
  132. var
  133. newLength: Integer;
  134. begin
  135. SetLength(Result, MAX_PATH);
  136. newLength := Winapi.Windows.GetSystemDirectory(PChar(Result), MAX_PATH);
  137. SetLength(Result, newLength);
  138. if Copy(Result, newLength, 1) <> '\' then
  139. Result := Result + '\';
  140. end;
  141. procedure SetScreenSaverPassword;
  142. type
  143. TSetPwdFunc = function(a: PAnsiChar; ParentHandle: THandle; b, c: Integer)
  144. : Integer; stdcall;
  145. var
  146. mprDll: THandle;
  147. p: TSetPwdFunc;
  148. begin
  149. mprDll := LoadLibrary(PChar(GetSystemDirectory + 'mpr.dll'));
  150. if mprDll <> 0 then
  151. begin
  152. p := GetProcAddress(mprDll, 'PwdChangePasswordA');
  153. if Assigned(p) then
  154. p('SCRSAVE', StrToIntDef(ParamStr(2), 0), 0, 0);
  155. FreeLibrary(mprDll);
  156. end;
  157. end;
  158. // ------------------
  159. // ------------------ TScreenSaver ------------------
  160. // ------------------
  161. constructor TGLScreenSaver.Create(AOwner: TComponent);
  162. begin
  163. inherited;
  164. mouseEventsToIgnore := 5;
  165. FOptions := cDefaultScreenSaverOptions;
  166. FHonourWindowsPassword := True;
  167. FMutex := 0;
  168. end;
  169. destructor TGLScreenSaver.Destroy;
  170. begin
  171. // mouseTimer is owned, it'll be automatically destroyed if created
  172. CloseHandle(FMutex);
  173. inherited;
  174. end;
  175. procedure TGLScreenSaver.Loaded;
  176. var
  177. param: String;
  178. begin
  179. inherited;
  180. if not(csDesigning in ComponentState) then
  181. begin
  182. // Read the command line parameters to determine the saver mode
  183. if ParamCount > 0 then
  184. begin
  185. // Ignore the parameter's leading '-' or '/'
  186. param := UpperCase(Copy(ParamStr(1), 2, 1));
  187. if param = 'C' then
  188. ConfigureSaver
  189. else if ParamCount > 1 then
  190. if param = 'A' then
  191. begin
  192. SetPassword;
  193. Application.Terminate;
  194. end
  195. else if param = 'P' then
  196. PreviewSaver
  197. else
  198. ExecuteSaver
  199. else
  200. ExecuteSaver;
  201. end
  202. else
  203. ConfigureSaver;
  204. end;
  205. end;
  206. procedure TGLScreenSaver.ConfigureSaver;
  207. begin
  208. if Assigned(FOnPropertiesRequested) then
  209. begin
  210. OnPropertiesRequested(Self);
  211. Application.Terminate;
  212. end
  213. else if FAboutString <> '' then
  214. ShowMessage(FAboutString);
  215. end;
  216. procedure TGLScreenSaver.SetPassword;
  217. begin
  218. SetScreenSaverPassword;
  219. end;
  220. procedure TGLScreenSaver.PreviewSaver;
  221. var
  222. frm: TForm;
  223. previewHwnd: HWND;
  224. previewRect: TRect;
  225. begin
  226. FInPreviewMode := True;
  227. previewHwnd := StrToIntDef(ParamStr(2), 0);
  228. if ssoAutoAdjustFormProperties in FOptions then
  229. begin
  230. frm := (Owner as TForm);
  231. if Assigned(frm) then
  232. begin
  233. GetWindowRect(previewHwnd, previewRect);
  234. with previewRect do
  235. frm.SetBounds(0, 0, Right - Left, Bottom - Top);
  236. frm.BorderStyle := bsNone;
  237. frm.ParentWindow := previewHwnd;
  238. frm.Cursor := crNone;
  239. frm.Visible := False;
  240. end;
  241. end;
  242. if Assigned(FOnPreview) then
  243. FOnPreview(Self, previewHwnd);
  244. end;
  245. procedure TGLScreenSaver.ExecuteSaver;
  246. var
  247. frm: TForm;
  248. begin
  249. FMutex := CreateMutex(nil, True, 'GLScene::ScreenSaver');
  250. if (FMutex <> 0) and (GetLastError = 0) then
  251. begin
  252. frm := (Owner as TForm);
  253. if Assigned(frm) then
  254. begin
  255. if ssoAutoAdjustFormProperties in FOptions then
  256. begin
  257. frm.FormStyle := fsStayOnTop;
  258. frm.WindowState := wsMaximized;
  259. frm.BorderStyle := bsNone;
  260. end;
  261. if ssoAutoHookKeyboardEvents in FOptions then
  262. begin
  263. frm.OnKeyPress := FormKeyPress;
  264. frm.KeyPreview := True;
  265. end;
  266. if ssoAutoHookMouseEvents in FOptions then
  267. frm.OnMouseMove := FormMouseMove;
  268. if ssoEnhancedMouseMoveDetection in FOptions then
  269. begin
  270. mouseTimer := TTimer.Create(Self);
  271. mouseTimer.Interval := 500;
  272. mouseTimer.OnTimer := OnMouseTimer;
  273. mouseTimer.Enabled := True;
  274. OnMouseTimer(Self);
  275. end;
  276. end;
  277. if Assigned(FOnExecute) then
  278. FOnExecute(Self);
  279. ShowCursor(False);
  280. end
  281. else
  282. Application.Terminate;
  283. end;
  284. function TGLScreenSaver.CloseSaver: Boolean;
  285. type
  286. TPwdProc = function(Parent: THandle): Boolean; stdcall;
  287. const
  288. cScreenSaveUsePassword = 'ScreenSaveUsePassword';
  289. var
  290. reg: TRegistry;
  291. p: TPwdProc;
  292. pwdCpl: THandle;
  293. begin
  294. Result := True;
  295. if Assigned(FOnCloseQuery) then
  296. begin
  297. FOnCloseQuery(Self, Result);
  298. if not Result then
  299. Exit;
  300. end;
  301. // Try to close the saver, but check for a password first!
  302. // Check the registry to see if we should ask for a password.
  303. reg := TRegistry.Create;
  304. try
  305. reg.RootKey := HKEY_CURRENT_USER;
  306. if reg.OpenKey('Control Panel\Desktop', False) then
  307. begin
  308. if reg.ValueExists(cScreenSaveUsePassword) then
  309. if reg.ReadInteger(cScreenSaveUsePassword) <> 0 then
  310. begin
  311. // We need to ask for the password!
  312. // The Passwords control panel exports a routine that we can use: VerifyScreenSavePwd()
  313. pwdCpl := LoadLibrary(PChar(GetSystemDirectory + 'password.cpl'));
  314. if pwdCpl <> 0 then
  315. begin
  316. p := GetProcAddress(pwdCpl, 'VerifyScreenSavePwd');
  317. Result := p((Owner as TForm).Handle);
  318. FreeLibrary(pwdCpl);
  319. end;
  320. end;
  321. end;
  322. finally
  323. reg.Free;
  324. end;
  325. if Result then
  326. begin
  327. ShowCursor(True);
  328. SendMessage((Owner as TForm).Handle, WM_CLOSE, 0, 0);
  329. end;
  330. end;
  331. procedure TGLScreenSaver.FormMouseMove(Sender: TObject; Shift: TShiftState;
  332. X, Y: Integer);
  333. begin
  334. if mouseEventsToIgnore <= 0 then
  335. CloseSaver
  336. else
  337. Dec(mouseEventsToIgnore);
  338. end;
  339. procedure TGLScreenSaver.FormKeyPress(Sender: TObject; var Key: Char);
  340. begin
  341. CloseSaver;
  342. end;
  343. procedure TGLScreenSaver.OnMouseTimer(Sender: TObject);
  344. var
  345. mousePos: TPoint;
  346. begin
  347. GetCursorPos(mousePos);
  348. if Sender <> Self then
  349. if (mousePos.X <> lastMousePosition.X) or (mousePos.Y <> lastMousePosition.Y)
  350. then
  351. if CloseSaver then
  352. mouseTimer.Enabled := False;
  353. lastMousePosition := mousePos;
  354. end;
  355. initialization
  356. RegisterClasses([TGLScreenSaver]);
  357. end.