Полный исходный код данного хранителя приведен в конце совета. Ну а теперь обсудим подробности создания этого типа программ, являющихся ровесниками первого компьютера.
Вступление
Хранитель экрана Windows является обыкновенным исполнимым приложением Windows, имеющим в отличие от стандартных программ расширение .SCR. Тем не менее, для корректного связывания с панелью управления, хранитель должен соблюдать определенные требования. В общих чертах программа должна:
Ниже я попытаюсь показать, как каждое из этих требований может быть удовлетворено с помощью Delphi.
С самого началаХранитель экрана, который мы собираемся создать, во время очередного простоя компьютера очищает экран и рисует затененные сферы в произвольных местах экрана, периодически их стирая и начиная заново. Пользователь может определить максимальное количество выводимых на экран сфер и скорость их рисования.
Прежде всего создайте новый, пустой проект, выбрав пункт New Project из меню File. (Если вы находитесь в репозитарии, выберите "Blank project".)
Конфигурационная формаПервое, что видит большинство людей при запуске хранителя экрана - диалог настройки. В нем пользователь может определить значения для специфических опций хранителя экрана. Для того, чтобы создать такую форму, измените свойства Form1 (создается автоматически при создании нового проекта) как показано ниже:
BorderIcons [biSystemMenu] biSystemMenu True biMinimize False biMaximize False BorderStyle bsDialog Caption Configuration Height 162 Name CfgFrm Position poScreenCenter Visible False Width 266Нам необходимо предоставить возможность изменять максимальное количество сфер, выводимых на экране, их размер и скорость рисования. Для того, чтобы это сделать, добавьте следующие три компонента Label (из палитры Standard) и компонент SpinEdit (из палитры Samples): (Примечание: Для быстрого размещения этих компонентов на форме скопируйте этот текст в буфер обмена и замените текст описания формы, выводимый при нажатии на пункт меню "View as Text" контекстного меню формы.)
object Label1: TLabel Left = 16Top = 19Width = 58Height = 16Alignment = taRightJustifyCaption = 'Сфер:'end object Label2: TLabel Left = 41Top = 59Width = 33Height = 16Alignment = taRightJustifyCaption = 'Размер:'end object Label3: TLabel Left = 29Top = 99Width = 45Height = 16Alignment = taRightJustifyCaption = 'Скорость:'end object spnSpheres: TSpinEdit Left = 84Top = 15Width = 53Height = 26MaxValue = 500MinValue = 1TabOrder = 0Value = 50end object spnSize: TSpinEdit Left = 84Top = 55Width = 53Height = 26MaxValue = 250MinValue = 50TabOrder = 1Value = 100end object spnSpeed: TSpinEdit Left = 84Top = 95Width = 53Height = 26MaxValue = 10MinValue = 1TabOrder = 2Value = 10end |
|
object btnOK: TBitBtn Left = 153Top = 11Width = 89Height = 34TabOrder = 3Kind = bkOKend object btnCancel: TBitBtn Left = 153Top = 51Width = 89Height = 34TabOrder = 4Kind = bkCancelend object btnTest: TBitBtn Left = 153Top = 91Width = 89Height = 34Caption = 'Тест...'TabOrder = 5Kind = bkIgnoreend |
uses IniFiles; |
procedure LoadConfig;procedure SaveConfig; |
const CfgFile = 'SPHERES.INI'; procedure TCfgFrm.LoadConfig; var inifile : TIniFile;begin inifile := TIniFile.Create(CfgFile);trywith inifile do beginspnSpheres.Value := ReadInteger('Config', 'Spheres', 50);spnSize.Value := ReadInteger('Config', 'Size', 100);spnSpeed.Value := ReadInteger('Config', 'Speed', 10);end;finallyinifile.Free;end;end; {TCfgFrm.LoadConfig} procedure TCfgFrm.SaveConfig; var inifile : TIniFile;begin inifile := TIniFile.Create(CfgFile);trywith inifile do beginWriteInteger('Config', 'Spheres', spnSpheres.Value);WriteInteger('Config', 'Size', spnSize.Value);WriteInteger('Config', 'Speed', spnSpeed.Value);end;finallyinifile.Free;end;end; {TCfgFrm.SaveConfig} |
procedure TCfgFrm.FormCreate(Sender: TObject); begin LoadConfig;end; {TCfgFrm.FormCreate} |
procedure TCfgFrm.btnOKClick(Sender: TObject); begin SaveConfig;Close;end; {TCfgFrm.btnOKClick} |
procedure TCfgFrm.btnCancelClick(Sender: TObject); begin Close;end; {TCfgFrm.btnCancelClick} |
procedure TCfgFrm.btnTestClick(Sender: TObject); begin ScrnFrm.Show;end; {TCfgFrm.btnTestClick} |
object tmrTick: TTimer Enabled = FalseOnTimer = tmrTickTimerLeft = 199Top = 122 end |
procedure DrawSphere(x, y, size : integer; color : TColor); |
procedure TScrnFrm.DrawSphere(x, y, size : integer; color : TColor); var i, dw : integer;cx, cy : integer;xy1, xy2 : integer;r, g, b : byte;begin with Canvas do begin{Заполняем установки карандаша и кисти.}Pen.Style := psClear;Brush.Style := bsSolid;Brush.Color := color;{Подготовим цвета для сфер.}r := GetRValue(color);g := GetGValue(color);b := GetBValue(color);{Рисуем сферу.}dw := size div 16;for i := 0 to 15 do beginxy1 := (i * dw) div 2;xy2 := size - xy1;Brush.Color := RGB(Min(r + (i * 8), 255), Min(g + (i * 8), 255),Min(b + (i * 8), 255));Ellipse(x + xy1, y + xy1, x + xy2, y + xy2);end;end;end; {TScrnFrm.DrawSphere} |
function Min(a, b : integer) : integer; begin if b < a thenResult := belseResult := a;end; {Min} |
procedure TScrnFrm.tmrTickTimer(Sender: TObject); const sphcount : integer = 0;var x, y : integer;size : integer;r, g, b : byte;color : TColor;begin if sphcount > CfgFrm.spnSpheres.Value then beginRefresh;sphcount := 0;end;Inc(sphcount);x := Random(ClientWidth);y := Random(ClientHeight);size := CfgFrm.spnSize.Value + Random(50) - 25;x := x - size div 2;y := y - size div 2;r := Random($80);g := Random($80);b := Random($80);DrawSphere(x, y, size, RGB(r, g, b));end; {TScrnFrm.tmrTickTimer} |
uses Cfg; |
var crs : TPoint; |
procedure DeactivateScrnSaver(var Msg : TMsg; var Handled : boolean); |
procedure TScrnFrm.DeactivateScrnSaver(var Msg : TMsg; var Handled : boolean); var done : boolean;begin if Msg.message = WM_MOUSEMOVE thendone := (Abs(LOWORD(Msg.lParam) - crs.x) > 5) or(Abs(HIWORD(Msg.lParam) - crs.y) > 5)elsedone := (Msg.message = WM_KEYDOWN) or (Msg.message = WM_ACTIVATE) or(Msg.message = WM_ACTIVATEAPP) or (Msg.message = WM_NCACTIVATE);if done thenClose;end; {TScrnFrm.DeactivateScrnSaver} |
procedure TScrnFrm.FormShow(Sender: TObject); begin GetCursorPos(crs);tmrTick.Interval := 1000 - CfgFrm.spnSpeed.Value * 90;tmrTick.Enabled := true;Application.OnMessage := DeactivateScrnSaver;ShowCursor(false);end; {TScrnFrm.FormShow} |
procedure TScrnFrm.FormHide(Sender: TObject); begin Application.OnMessage := nil;tmrTick.Enabled := false;ShowCursor(true);end; {TScrnFrm.FormHide} |
procedure TScrnFrm.FormActivate(Sender: TObject); begin WindowState := wsMaximized;end; {TScrnFrm.FormActivate} |
{$D SCRNSAVE Spheres Screen Saver} |
begin if (ParamCount > 0) and (UpperCase(ParamStr(1)) = '/S') then begin{ScrnFrm должна быть главной формой.}Application.CreateForm(TScrnFrm, ScrnFrm);Application.CreateForm(TCfgFrm, CfgFrm);end else begin{CfgFrm должна быть главной формой.}Application.CreateForm(TCfgFrm, CfgFrm);Application.CreateForm(TScrnFrm, ScrnFrm);end;Application.Run;end. |
uses Forms, SysUtils,Scrn in 'SCRN.PAS' {ScrnFrm},Cfg in 'CFG.PAS' {CfgFrm}; |
begin {Возможен запуск только одной копии.}if hPrevInst = 0 then beginif (ParamCount > 0) and (UpperCase(ParamStr(1)) = '/S') then begin...end;Application.Run;end;end; |
object CfgFrm: TCfgFrm Left = 196Top = 124BorderIcons = [biSystemMenu]BorderStyle = bsDialogCaption = 'Конфигурация'ClientHeight = 135ClientWidth = 258Font.Color = clWindowTextFont.Height = -13Font.Name = 'System'Font.Style = []PixelsPerInch = 96Position = poScreenCenterOnCreate = FormCreateTextHeight = 16object Label1: TLabelLeft = 16Top = 19Width = 58Height = 16Alignment = taRightJustifyCaption = 'Сфер:'endobject Label2: TLabelLeft = 41Top = 59Width = 33Height = 16Alignment = taRightJustifyCaption = 'Размер:'endobject Label3: TLabelLeft = 29Top = 99Width = 45Height = 16Alignment = taRightJustifyCaption = 'Скорость:'endobject spnSpheres: TSpinEditLeft = 84Top = 15Width = 53Height = 26MaxValue = 500MinValue = 1TabOrder = 0Value = 50endobject spnSize: TSpinEditLeft = 84Top = 55Width = 53Height = 26MaxValue = 250MinValue = 50TabOrder = 1Value = 100endobject spnSpeed: TSpinEditLeft = 84Top = 95Width = 53Height = 26MaxValue = 10MinValue = 1TabOrder = 2Value = 10endobject btnOK: TBitBtnLeft = 153Top = 11Width = 89Height = 34TabOrder = 3OnClick = btnOKClickKind = bkOKendobject btnCancel: TBitBtnLeft = 153Top = 51Width = 89Height = 34TabOrder = 4OnClick = btnCancelClickKind = bkCancelendobject btnTest: TBitBtnLeft = 153Top = 91Width = 89Height = 34Caption = 'Тест...'TabOrder = 5OnClick = btnTestClickKind = bkIgnoreendend |
unit Cfg; interface uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,Forms, Dialogs, StdCtrls, Buttons, Spin; type TCfgFrm = class(TForm)Label1: TLabel;Label2: TLabel;Label3: TLabel;spnSpheres: TSpinEdit;spnSize: TSpinEdit;spnSpeed: TSpinEdit;btnOK: TBitBtn;btnCancel: TBitBtn;btnTest: TBitBtn;procedure FormCreate(Sender: TObject);procedure btnOKClick(Sender: TObject);procedure btnCancelClick(Sender: TObject);procedure btnTestClick(Sender: TObject);private{ Private declarations }procedure LoadConfig;procedure SaveConfig;public{ Public declarations }end; var CfgFrm: TCfgFrm; implementation {$R *.DFM} uses Scrn,IniFiles; const CfgFile = 'SPHERES.INI'; procedure TCfgFrm.LoadConfig; var inifile : TIniFile;begin inifile := TIniFile.Create(CfgFile);trywith inifile do beginspnSpheres.Value := ReadInteger('Config', 'Spheres', 50);spnSize.Value := ReadInteger('Config', 'Size', 100);spnSpeed.Value := ReadInteger('Config', 'Speed', 10);end;finallyinifile.Free;end;end; {TCfgFrm.LoadConfig} procedure TCfgFrm.SaveConfig; var inifile : TIniFile;begin inifile := TIniFile.Create(CfgFile);trywith inifile do beginWriteInteger('Config', 'Spheres', spnSpheres.Value);WriteInteger('Config', 'Size', spnSize.Value);WriteInteger('Config', 'Speed', spnSpeed.Value);end;finallyinifile.Free;end;end; {TCfgFrm.SaveConfig} procedure TCfgFrm.FormCreate(Sender: TObject); begin LoadConfig;end; {TCfgFrm.FormCreate} procedure TCfgFrm.btnOKClick(Sender: TObject); begin SaveConfig;Close;end; {TCfgFrm.btnOKClick} procedure TCfgFrm.btnCancelClick(Sender: TObject); begin Close;end; {TCfgFrm.btnCancelClick} procedure TCfgFrm.btnTestClick(Sender: TObject); begin ScrnFrm.Show;end; {TCfgFrm.btnTestClick} end. |
object ScrnFrm: TScrnFrm Left = 196Top = 98BorderIcons = []BorderStyle = bsNoneCaption = 'ScrnFrm'ClientHeight = 101ClientWidth = 259Color = clBlackFont.Charset = DEFAULT_CHARSETFont.Color = clWindowTextFont.Height = -13Font.Name = 'System'Font.Style = []FormStyle = fsStayOnTopOldCreateOrder = TrueOnActivate = FormActivateOnHide = FormHideOnShow = FormShowPixelsPerInch = 96TextHeight = 16object tmrTick: TTimerEnabled = FalseOnTimer = tmrTickTimerLeft = 65535Top = 2endend |
unit Scrn; interface uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,Forms, Dialogs, ExtCtrls; type TScrnFrm = class(TForm)tmrTick: TTimer;procedure tmrTickTimer(Sender: TObject);procedure FormShow(Sender: TObject);procedure FormHide(Sender: TObject);procedure FormActivate(Sender: TObject);private{ Private declarations }procedure DrawSphere(x, y, size : integer; color : TColor);procedure DeactivateScrnSaver(var Msg : TMsg; var Handled : boolean);public{ Public declarations }end; var ScrnFrm: TScrnFrm; implementation {$R *.DFM} uses Cfg; var crs : TPoint; { Оригинальная позиция курсора мыши.} function Min(a, b : integer) : integer; begin if b < a thenResult := belseResult := a;end; {Min} procedure TScrnFrm.DrawSphere(x, y, size : integer; color : TColor); var i, dw : integer;cx, cy : integer;xy1, xy2 : integer;r, g, b : byte;begin with Canvas do begin{Заполняем установки карандаша и кисти.}Pen.Style := psClear;Brush.Style := bsSolid;Brush.Color := color;{Подготовим цвета для сфер.}r := GetRValue(color);g := GetGValue(color);b := GetBValue(color);{Рисуем сферу.}dw := size div 16;for i := 0 to 15 do beginxy1 := (i * dw) div 2;xy2 := size - xy1;Brush.Color := RGB(Min(r + (i * 8), 255), Min(g + (i * 8), 255),Min(b + (i * 8), 255));Ellipse(x + xy1, y + xy1, x + xy2, y + xy2);end;end;end; {TScrnFrm.DrawSphere} procedure TScrnFrm.DeactivateScrnSaver(var Msg : TMsg; var Handled : boolean); var done : boolean;begin if Msg.message = WM_MOUSEMOVE thendone := (Abs(LOWORD(Msg.lParam) - crs.x) > 5) or(Abs(HIWORD(Msg.lParam) - crs.y) > 5)elsedone := (Msg.message = WM_KEYDOWN) or (Msg.message = WM_KEYUP) or(Msg.message = WM_SYSKEYDOWN) or (Msg.message = WM_SYSKEYUP) or(Msg.message = WM_ACTIVATE) or (Msg.message = WM_NCACTIVATE) or(Msg.message = WM_ACTIVATEAPP) or (Msg.message = WM_LBUTTONDOWN) or(Msg.message = WM_RBUTTONDOWN) or (Msg.message = WM_MBUTTONDOWN);if done thenClose;end; {TScrnFrm.DeactivateScrnSaver} procedure TScrnFrm.tmrTickTimer(Sender: TObject); const sphcount : integer = 0;var x, y : integer;size : integer;r, g, b : byte;color : TColor;begin if sphcount > CfgFrm.spnSpheres.Value then beginRefresh;sphcount := 0;end;Inc(sphcount);x := Random(ClientWidth);y := Random(ClientHeight);size := CfgFrm.spnSize.Value + Random(50) - 25;x := x - size div 2;y := y - size div 2;r := Random($80);g := Random($80);b := Random($80);DrawSphere(x, y, size, RGB(r, g, b));end; {TScrnFrm.tmrTickTimer} procedure TScrnFrm.FormShow(Sender: TObject); begin GetCursorPos(crs);tmrTick.Interval := 1000 - CfgFrm.spnSpeed.Value * 90;tmrTick.Enabled := true;Application.OnMessage := DeactivateScrnSaver;ShowCursor(false);end; {TScrnFrm.FormShow} procedure TScrnFrm.FormHide(Sender: TObject); begin Application.OnMessage := nil;tmrTick.Enabled := false;ShowCursor(true);end; {TScrnFrm.FormHide} procedure TScrnFrm.FormActivate(Sender: TObject); begin WindowState := wsMaximized;end; {TScrnFrm.FormActivate} end. |
program Spheres; uses Forms, SysUtils,Scrn in 'SCRN.PAS' {ScrnFrm},Cfg in 'CFG.PAS' {CfgFrm}; {$R *.RES} {$ D SCRNSAVE Spheres Screen Saver} begin {Возможен запуск только одной копии.}if hPrevInst = 0 then beginif (ParamCount > 0) and (UpperCase(ParamStr(1)) = '/S') then begin{ScrnFrm должна быть главной формой.}Application.CreateForm(TScrnFrm, ScrnFrm);Application.CreateForm(TCfgFrm, CfgFrm);end else begin{CfgFrm должна быть главной формой.}Application.CreateForm(TCfgFrm, CfgFrm);Application.CreateForm(TScrnFrm, ScrnFrm);end;Application.Run;end;end. |
[Compiler] A=1 B=0 D=1 F=0 I=1 K=1 L=1 P=1 Q=0 R=0 S=1 T=0 U=1 V=1 W=0 X=1 Y=1 [Linker] MapFile=0 LinkBuffer=0 DebugInfo=0 OptimizeExe=1 StackSize=16384 HeapSize=8192 [Directories] OutputDir= SearchPath= Conditionals= [Parameters] RunParams=/s |