GXS.ScreenSaver.pas 12 KB

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