본문 바로가기
IT-개발,DB

[개발] [강좌][번역본]델파이로 윈도우즈 스크린 세이버 만들기

by SB리치퍼슨 2010. 10. 14.


델파이로 윈도우즈 스크린 세이버 만들기 
소백촌닭  (홈페이지)  2001-06-15 13:59:03, 조회 : 511, 평가 :  없음
- 연결 #1 : http://sobakcc.com/dev/lecture/spheres.zip


델파이로 윈도우즈 스크린 세이버 만들기

------------------------------------------------------------------------------

How to Make a Windows Screen Saver in Delphi
by Mark R. Johnson

원문은 Mark R. Johnson 에 의하여 작성됨.

번역 배경환 ( sobakcc@intizen.com )

------------------------------------------------------------------------------

때때로, 난 제어판의 디스플레이에서 선택될 수 있는 Delphi로 Windows screen
saver를 작성하는 방법에 대하여 질문을 받곤 한다. 단지 부분적으로 질문에
대답했던 약간은 일반적인 반응을 본 후, 난 내가 직접 만들기로 결심했다. 여기서
보게될 코드는 단순한 windows screen saver이다.

스크린 세이버를 위한 완벽한 델파이 소스 코드는spheres.zip(4k)를 FTP로 다운
받을 수 있다. 어쨌든 코드에 대한 자세한 것을 보기 전에, 내가 이 기사를 쓰는데
도움을 얻을 수 있었던 comp.lang.pascal 에 W.Wolf 가 올린 일반적인 screen
saver tip에 대해 고맙게 생각하고 있다.

Background

Windows screen saver는 기본적으로 .SCR 파일 확장자를 갖도록 변경된 표준
윈도우즈 실행가능한 파일이다. 제어판->디스플레이으로 적당히 인터페이스하기
위해서는 어쨌든, 어떤 요구사항이 만족되어야 한다. 일반적으로 그 프로그램은
다음과 같아야 한다.

- optional settings을 갖는다.
- 스스로 설명을 제공한다.
- active mode와 configuration mode를 구별한다.
- 다중 실행을 스스로 막는다.
- 사용자가 키를 누르거나 마우스의 움직일 때 종료한다.

난 이러한 요구사항들을 Delphi를 사용해서 만족시킬 수 있을지를 보여주려고 한다.


Getting Started

우리가 만들려는 screen saver는 screen을 공백으로 만들 것이고 screen상에
랜덤하게 shaded spheres를 그려나갈 것이다. 그것도 주기적으로 지우고 다시
시작하면서. 사용자는 그려나가는 size와 speed 뿐만 아니라 지우기 전에 그려나갈
최대 개수의 sphere를 정의할 수 있다.

새로운 프로젝트를 시작하기 위해서 Delphi File메뉴로부터 New Project를 선택한다.


Configuration Form

screen saver에서 보게되는 첫 번째는 setup dialog이다. 이것은 사용자가
screen saver에 특정한 options에 대하여 값을 설정하는 곳이다. 이 같은 form을
생성하기 위해서 Form1의 속성을 다음과 같이 바꾸어야 한다.(new project를
시작할 때 자동적으로 생성되는 Form이 Form1이다)

BorderIcons     [biSystemMenu]
  biSystemMenu  True
  biMinimize    False
  biMaximize    False
BorderStyle     bsDialog
Caption         Configuration
Height          162
Name            CfgFrm
Position        poScreenCenter
Visible         False
Width           266

우리들은 spheres가 그려지는 speed와 spheres의 size, screen상에 그려진
spheres의 최대 개수를 설정할 수 있도록 한다. 이를 구현하기 위해서, 다음의
3개의 TLabel과 SpinEdits를 추가한다.

object Label1: TLabel
  Left = 16
  Top = 19
  Width = 58
  Height = 16
  Alignment = taRightJustify
  Caption = 'Spheres:'
end
object Label2: TLabel
  Left = 41
  Top = 59
  Width = 33
  Height = 16
  Alignment = taRightJustify
  Caption = 'Size:'
end
object Label3: TLabel
  Left = 29
  Top = 99
  Width = 45
  Height = 16
  Alignment = taRightJustify
  Caption = 'Speed:'
end
object spnSpheres: TSpinEdit
  Left = 84
  Top = 15
  Width = 53
  Height = 26
  MaxValue = 500
  MinValue = 1
  TabOrder = 0
  Value = 50
end
object spnSize: TSpinEdit
  Left = 84
  Top = 55
  Width = 53
  Height = 26
  MaxValue = 250
  MinValue = 50
  TabOrder = 1
  Value = 100
end
object spnSpeed: TSpinEdit
  Left = 84
  Top = 95
  Width = 53
  Height = 26
  MaxValue = 10
  MinValue = 1
  TabOrder = 2
  Value = 10
end

마지막으로 우리는 3 개의 버튼 : OK, Cancel, Test가 필요하다. Test 버튼은
screen saver setup dialog에 대하여 표준은 아니지만, 구현이 편리하고 쉽다.
아래에 BitButton을 사용해서 3개의 버튼을 추가해보자.

object btnOK: TBitBtn
  Left = 153
  Top = 11
  Width = 89
  Height = 34
  TabOrder = 3
  Kind = bkOK
end
object btnCancel: TBitBtn
  Left = 153
  Top = 51
  Width = 89
  Height = 34
  TabOrder = 4
  Kind = bkCancel
end
object btnTest: TBitBtn
  Left = 153
  Top = 91
  Width = 89
  Height = 34
  Caption = 'Test...'
  TabOrder = 5
  Kind = bkIgnore
end

우리가 form 레이아웃을 갖는다면, 추가할 코드를 작성할 필요가 있다. 먼저,
현재의 설정을 로드하고 저장할 필요가 있다. 이를 구현하기 위해서 Spheres, size,
그리고 speed 값을 사용자의 windows directory에 (*.INI)파일로 위치시켜야 한다.
Delphi의 TIniFile 객체가 이를 쉽게 구현할 수 있도록 해준다.

Setup form에 대한 code view로 바꾸어서 configuration form unit의
implementation section의 uses 절에 다음 코드를 추가하자.

uses
  IniFiles;

그리고 나서, TCfgFrm 클래스 선언의 private section에 다음 procedure를 선언한다.

    procedure LoadConfig;
    procedure SaveConfig;

이제 implementation section에서 uses 절 이후에 procedure 정의를 추가한다.

const
  CfgFile = 'SPHERES.INI';

procedure TCfgFrm.LoadConfig;
var
   inifile : TIniFile;
begin
  inifile := TIniFile.Create(CfgFile);
  try
    with inifile do begin
      spnSpheres.Value := ReadInteger('Config', 'Spheres', 50);
      spnSize.Value    := ReadInteger('Config', 'Size', 100);
      spnSpeed.Value   := ReadInteger('Config', 'Speed', 10);
    end;
  finally
    inifile.Free;
  end;
end; {TCfgFrm.LoadConfig}

procedure TCfgFrm.SaveConfig;
var
   inifile : TIniFile;
begin
  inifile := TIniFile.Create(CfgFile);
  try
    with inifile do begin
      WriteInteger('Config', 'Spheres', spnSpheres.Value);
      WriteInteger('Config', 'Size', spnSize.Value);
      WriteInteger('Config', 'Speed', spnSpeed.Value);
    end;
  finally
    inifile.Free;
  end;
end; {TCfgFrm.SaveConfig}

configuration form에 대하여 남아있는 것은 configuration을 적절히 load하고
save하는 이벤트이다. 먼저, 프로그램이 시작할 때 자동적으로 configuration을
load할 수 있도록 setup form의 OnCreate 이벤트로 이를 구현한다.
object inspector의 events tab에서 OnCreate field를 double-click한다.
그리고 다음의 코드를 입력한다.

procedure TCfgFrm.FormCreate(Sender: TObject);
begin
  LoadConfig;
end; {TCfgFrm.FormCreate}

다음은 form에서 OK 버튼을 더블 클릭한다. 현재의 configuration을 저장하기
위하여 OK가 클릭되면 configuration을 저장하고 window를 종료한다. 다음 코드를
추가하면 된다.

procedure TCfgFrm.btnOKClick(Sender: TObject);
begin
  SaveConfig;
  Close;
end; {TCfgFrm.btnOKClick}

Cancel 버튼을 눌렀을 때 저장하지 않고 form을 닫기 위해서는 form에서 Cancel
버튼을 double-click하고 다음 코드를 추가한다.

procedure TCfgFrm.btnCancelClick(Sender: TObject);
begin
  Close;
end; {TCfgFrm.btnCancelClick}

마지막으로, screen saver를 test하기 위해서 screen saver form을 보여주도록
하여야 할 것이다. 먼저, form에서 test버튼을 double-click해서 다음 코드를
추가한다.

procedure TCfgFrm.btnTestClick(Sender: TObject);
begin
  ScrnFrm.Show;
end; {TCfgFrm.btnTestClick}

implementation section에서 uses 절에 Scrn을 추가한다.
Scrn은 다음 단계에서 작성할 screen saver form unit을 참조하기 위한 것이다.
이제 까지의 작업을 File메뉴의 Save File As를 선택해서 Cfg로서 form unit을
저장하도록 하자.


Screen Saver Form

screen saver는 단순하게 전 screen을 채우는 크고, 검고, caption이 없는 form이다.
두번째 폼을 생성하기 위해서 파일메뉴나 Browse Gallery에서 New Form을 선택한다.

BorderIcons     []
  biSystemMenu  False
  biMinimize    False
  biMaximize    False
BorderStyle     bsNone
Color           clBlack
FormStyle       fsStayOnTop
Name            ScrnFrm
Visible         False

이 form에 대해서 한 개의 컴포넌트를 추가한다. 바로 TTimer 컴포넌트이다.
다음과 같이 적절하게 속성을 정의하자.

object tmrTick: TTimer
  Enabled = False
  OnTimer = tmrTickTimer
  Left = 199
  Top = 122
end

다른 컴포넌트는 이 form에 필요하지 않다. 그러나, shaded spheres를 그리는 것을
처리하기 위한 약간의 코드가 필요하다는 것을 알 수 있다. frmScrn form에서
code window로 이동하자. TfrmScrn의 private section에서 다음의 procedure를
추가한다.

    procedure DrawSphere(x, y, size : integer; color : TColor);

이제 unit의 implementation section에서 procedure 정의를 추가한다.

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
    {Fill in the pen & brush settings.}
    Pen.Style := psClear;
    Brush.Style := bsSolid;
    Brush.Color := color;
    {Prepare colors for sphere.}
    r := GetRValue(color);
    g := GetGValue(color);
    b := GetBValue(color);
    {Draw the sphere.}
    dw := size div 16;
    for i := 0 to 15 do begin
      xy1 := (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}

코드에서 볼 수 있는 것처럼, (x,y)좌표는 지름과 base color뿐만 아니라,
sphere의 (left,top)코너를 지시한다. sphere를 그리기 위해서, 주어진 base
color로 시작해서 밝게 증가하는 color의 brush로 단계적으로 처리한다.
매 새로운 brush로 이전의 circle로 더 작게 채워진 circle을 그려나간다.

기억해야 할 것은 Function이 또 다른 function인 Min()을 참조한다는 것이다.
이것은 델파이 표준 지원 함수이다. DrawSphere()에 대한 선언을 하기 전에,
unit에 다음을 추가 해야한다.

function Min(a, b : integer) : integer;
begin
  if b < a then
    Result := b
  else
    Result := a;
end; {Min}

DrawSphere() function을 주기적으로 호출하기 위해서는 Timer 컴포넌트의
OnTimer이벤트를 frmScrn에서 사용해야 한다. form상에서 TTimer 컴포넌트를
double-click해서 아래의 코드를 추가하자.

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 begin
    Refresh;
    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}

이 메소드는 sphcount에서 그려진 sphere의 수를 추적하고 sphere가 최대 개수가
되면 screen에서 지워준다(refresh). 그리고 다음에 그려질 sphere에 대한
random position, size, 그리고 color 를 계산한다.(color의 범위는 그림자를
잘 표현 하도록 밝은 색상의 반을 사용한다.

위에서 나타난 것처럼, tmrTickTimer() procedure는 configuration options를
추적하기 위하여 CfgFrm form을 참조한다. 이 참조를 인지할 수 있도록,
unit의 implementation section에 대하여 uses 절에 다음을 추가한다.

uses
  Cfg;

다음으로 key가 눌렸을 때, 마우스가 움직였을 때나 screen saver form이 focus를
잃어버렸을 때 screen saver를 종료시키는 방법이다. 이를 수행하는 방법은
screen saver를 종료할 수 있도록 필요한 조건을 검색할 수 있는
Application.OnMessage 이벤트에 대한 handler를 생성하는 것이다.

먼저, unit의 implemetation section에 대하여 변수 선언을 추가하자.

var
  crs : TPoint;

이 변수는 나중에 비교될 원래의 마우스 커서 위치를 저장하기 위해 사용될 것이다.
이제 TfrmScrn 클래스의 private section에 다음을 선언하자.

    procedure DeactivateScrnSaver(var Msg : TMsg; var Handled : boolean);

unit의 implementation section에 다음 코드를 추가하자.

procedure TScrnFrm.DeactivateScrnSaver(var Msg : TMsg; var Handled : boolean);
var
  done : boolean;
begin
  if Msg.message = WM_MOUSEMOVE then
    done := (Abs(LOWORD(Msg.lParam) - crs.x) > 5) or
            (Abs(HIWORD(Msg.lParam) - crs.y) > 5)
  else
    done := (Msg.message = WM_KEYDOWN) or (Msg.message = WM_ACTIVATE) or
            (Msg.message = WM_ACTIVATEAPP) or (Msg.message = WM_NCACTIVATE);
  if done then
    Close;
end; {TScrnFrm.DeactivateScrnSaver}

WM_MOUSEMOVE window message를 수신할 때 원래 마우스 위치와 새 좌표를 비교한다.
5 픽셀 이상이 움직였다면 screen saver를 종료한다는 것이다. 그렇지 않고, key
가 다른 윈도우나 dialog box가 focus를 갖는다면, screen saver를 종료한다.

이 procedure를 더 효과적으로 하기 위해서 Application.OnMessage를 설정하고
마우스 커서의 원래 위치를 얻을 필요가 있다. form의 OnShow 이벤트 핸들러에
적당한 코드를 삽입하자.

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}

여기에 마우스 커서를 숨기는 것 뿐만 아니라 timer의 interval 값을 지정하고
이를 활성화하도록 한다. 다시, form의 OnHide 이벤트에서 이를 undone 하도록
한다.

procedure TScrnFrm.FormHide(Sender: TObject);
begin
  Application.OnMessage := nil;
  tmrTick.Enabled       := false;
  ShowCursor(true);
end; {TScrnFrm.FormHide}

마지막으로 screen saver form이 show될 때 전체 screen을 채운다는 것을 기억하자.
이는 OnActivate 이벤트에 코드를 삽입하여 처리한다.

procedure TScrnFrm.FormActivate(Sender: TObject);
begin
  WindowState := wsMaximized;
end; {TScrnFrm.FormActivate}

frmScrn form unit을 File 메뉴에서 Save File을 선택해서 Scrn.pas로 저장한다.


The Screen Saver Description

프로젝트 소스 파일에서 {$D scrnsaver_name} directive을 정의해서 screen saver
명칭을 Control Panel Desktop에 나타낼 수 있다. $D directive는 뒤에 나오는
스트링을 실행 파일의 module description entry로 삽입한다. 스트링을 인식하도록
Control Panel에 대해서 약어로 사용하는 "SCRNSAVE"로 시작해야 한다.

Delphi View메뉴로부터 Project Source를 선택해서 소스 파일을 에디트 할 수 있다.
directive "{$R *.RES}" 바로 아래에 다음 코드를 추가한다.

{$D SCRNSAVE Spheres Screen Saver}

스트링 "Spheres Screen Saver" 프로젝트를 완성하여 screen saver를 수행하면
Control Panel list에 나타날 것이다.


Active Versus Configuration Mode

Windows는 다음 두 조건으로 screen saver program을 수행하게 된다.
1) screen saver가 activation상태일 때, 2) screen saver가 configuration 상태일
때이다. 두 경우에 대해서 Windows는 동일한 프로그램을 수행하게 된다. 여기서
command line parameter로 "/s", active mode, 와 "/c", configuration mode, 로
구분된다. control panel에서 사용하려면 이 두가지 mode를 체크해야 한다.


Active Mode

screen saver를 active mode(/s)로 지정할 때, screen saver form을 생성하고
보여주어야 한다. 또한 configuration options의 모든 사항을 갖고 있기 때문에
configuration form을 생성할 필요도 있다. screen saver form이 닫힐 때 전체
프로그램은 종료되어야 한다. 이 경우에는 screen saver form을 Delphi Main
Form으로 정의를 한다.


Configuration Mode

screen saver가 configuration mode(/c)로 지정될 때, configuration form을
생성하고 보여주어야 한다.
configuration options를 test하기 위해서 screen saver form을 생성해야 주어야
한다. 그러나, configuration form이 닫힐 때, 전체 프로그램이 종료되어야
한다. 이 경우에는 Main Form을 configuration form으로 정의한다.


Defining the Main Form

원칙적으로는 /s command line상에 나타날 때, Main Form으로서 frmScrn을 지정해야
한다. 그리고 CfgFrm도 다른 경우에는 Main Form으로 지정되어야 한다. 이를 구현
하기 위해서는 TApplication VCL object의 문서화되지 않은 특징에 대한 지식이
필요하다. Main Form은 단순히 Application.CreateForm()에서 call되어 생성되는
첫 form이라는 것이다. 따라서, 실시간 조건에 따라 다른 Main form을 정의하기
위해서 프로젝트 소스를 다음과 같이 수정해야 한다.

begin
  if (ParamCount > 0) and (UpperCase(ParamStr(1)) = '/S') then begin
    {ScrnFrm needs to be the Main Form.}
    Application.CreateForm(TScrnFrm, ScrnFrm);
    Application.CreateForm(TCfgFrm, CfgFrm);
  end else begin
    {CfgFrm needs to be the Main Form.}
    Application.CreateForm(TCfgFrm, CfgFrm);
    Application.CreateForm(TScrnFrm, ScrnFrm);
  end;
  Application.Run;
end.

생성 순서를 변화시킴으로서, 자동적으로 instance에 대한 Main form을 지정할 수
있다. 게다가, Main form은 자동적으로 나타나게 되고, 두 form에 대하여 Visible
속성을 False로 정의하여야 한다. 결과적으로, 단지, 약간의 코드로 원하던 효과를
얻을 수 있다.


(Note: for the if statement to function as shown above, the "Complete
boolean eval" option should be unchecked in the Options | Project | Compiler
settings. Otherwise, an error will occur if the program is invoked with no
command line parameters.)

UpperCase() 델파이 메소드를 사용하기 위해서 SysUtils 문구가 다음처럼
프로젝트 파일의 Uses절에 포함되어야 한다.

uses
  Forms, SysUtils,
  Scrn in 'SCRN.PAS' {ScrnFrm},
  Cfg in 'CFG.PAS' {CfgFrm};

Blocking Multiple Instances

Windows screen saver에 대한 한가지 어려움은 실행중에 다중 인스턴스를 막아야
하는 것이다. 그렇지 않으면, 심지어 Windows는 인스턴스가 이미 실행중인 경우
에도 screen saver를 실행하게 될 것이다.

screen saver의 다중 인스턴스를 막기 위해서 아래에서 보는바와같이 프로젝트
소스파일에서 수정을 해야한다.


begin
  {Only one instance is allowed at a time.}
  if hPrevInst = 0 then begin
    if (ParamCount > 0) and (UpperCase(ParamStr(1)) = '/S') then begin
      ...
    end;
    Application.Run;
  end;
end;

hPrevInst변수는 현재 프로그램의 이전 인스턴스를 가리키는 Delphi에 의해 정의된
전역 변수이다.만약 수행중인 이전의 인스턴스가 없다면 zero가 될 것이다.

이제 프로젝트를 "spheres.dpr"로 저장하고 컴파일을 해라. 이제 스크린 세이버를
수행할 수 있어야 한다. command line 파라메터가 없다면 프로그램은 default인
configuration mode가 될 것이다. command line 파라메터로 "/s"를 주게 되면,
active mode로 test하게 될 것이다(Delphi Menu Run -> Parameters).


Installing the Screen Saver

프로그램의 테스트와 디버그 작업이 완료되었다면, 인스톨을 준비해야한다.
windows 디렉토리에 단순히 실행파일의 확장자를 .scr 로 변경하여 복사하기만
하면 된다(spheres.exe -> spheres.scr).
제어판에서 디스플레이를 더블클릭한다. 그리고 screen saver의 이름을 선택한다.
"Spheres Screen Saver"가 될 것이다. 그리고 확인 버튼을 클릭하면 된다.


Delphi is a registered trademark of Borland International.
------------------------------------------------------------------------------

원문 저자


keeper@mindspring.com
Copyright ⓒ 1995 Mark R. Johnson. This is a CITY ZOO production.
Last revised September 4, 1995.

번역자

배경환
sobakcc@intizen.com
http://sobakcc.inticity.com
2000년 3월 31일 금요일
 
 
 

반응형

댓글