1. Событие. Сообщение. Контекст.
  2. Количество кадров

Событие. Сообщение. Контекст.Наверх  

Начнем наш разговор с понятий "событие" и "сообщение".

Очень часто это синонимы одного и того же термина операционной системы, общающейся с приложениями посредством посылки сообщений. Код, написанный в проекте Delphi как обработчик события OnCreate, выполняется при получении приложением сообщения WM_CREATE, сообщению WM_PAINT соответствует событие OnPaint, и т.д..Такие события использует мнемонику, сходную с мнемоникой сообщений.

Как операционная система различает окна для осуществления диалога с ними? Все окна при своем создании регистрируются в операционной системе и получают уникальный идентификатор, называемый "ссылка на окно". Тип этой величины в Delphi - HWND (WiNDow Handle, ссылка на окно).

Ссылка на окно может использоваться не только операционной системой, но и приложениями для идентификации окна, с которым необходимо производить манипуляции.
Попробуем проиллюстрировать смысл ссылки на окно на несложном примере.

Откомпилируйте минимальное приложение Delphi и начните новый проект. Форму назовите Form2, разместите на форме кнопку, обработчик события OnClick кнопки приведите к следующему виду:

procedure
TForm2.Button1Click(Sender: TObject);
var
   H : HWND;
begin
   H := FindWindow ('TForm1', 'Form1');
   If H <> 0 then ShowMessage ('Есть Form1!')
         else ShowMessage ('Нет Form1!')
end;

Теперь при щелчке на кнопке выдается сообщение, есть ли запущенное приложение, класс окна которого зарегистрирован в операционной системе как 'TForm1', в заголовке которого записано 'Form1'. То есть если одновременно запустить обе наши программы, при нажатии на кнопку выдается одно сообщение, если окно с заголовком 'Form1' закрыть, при щелчке на кнопку выдается другое сообщение.

Здесь мы используем функцию API FindWindow, возвращающую величину типа HWND - ссылку на найденное окно либо ноль, если такое окно не найдено.

Итак, ссылка на окно однозначно определяет окно. Свойство Handle формы и есть эта ссылка, значение которой форма получает при выполнении функции API CreateWindow - создании окна. Имея ссылку на окно, операционная система общается с окном путем посылки сообщений-сигналов о том, что произошло какое-либо событие, имеющее отношение именно к этому окну. Если окно имеет намерение отреагировать на это событие, операционная система имеет это в виду и вместе с окном осуществляет эту реакцию. Окно может и не имея фокус получать сообщения и реагировать на них.
Проиллюстрируем это на примере.

Обработчик события OnMouseMove формы приведите к виду:

procedure TForm2.FormMouseMove(Sender: TObject; 
                      Shift: TShiftState; X,
   Y: Integer);
begin
  Caption := 'x=' + IntToStr (X) + ', y=' + IntToStr (Y)
end;

В заголовок формы выводятся координаты указателя мыши.
Запустите два экземпляра программы и обратите внимание, что окно, не имеющее фокус ("неактивное"), тоже реагирует на перемещение указателя по его поверхности.

Имея ссылку на окно, приложение может производить с ним любые действия, путем посылки ему сообщений.
Изменим код обработки щелчка кнопки:

procedure TForm2.Button1Click(Sender: TObject);
var
   H : HWND;
begin
   H := FindWindow ('TForm1', 'Form1');
   If H <> 0 then SendMessage (H, WM_CLOSE, 0, 0)
end;

Если имеется окно класса 'TForm1' с заголовком 'Form1', наше приложение посылает ему сообщение WM_CLOSE - пытается закрыть окно.
Точно также, если необходимо нарисовать что-либо на поверхности чужого окна, необходимо получить ссылку на это окно.

Для начала попробуем рисовать на поверхности родного окна.
Разместите еще одну кнопку, обработку щелчка которой приведите к виду:

procedure TForm2.Button2Click(Sender: TObject);
var

   dc : HDC;
begin
   dc := GetDC (Handle);
   Rectangle (dc, 10, 10, 110, 110);
   ReleaseDC (Handle, dc);
end;

Запустите приложение. При щелчке на добавленной кнопке на поверхности окна рисуется квадрат. Для рисования используем низкоуровневые функции Windows.
Попробуем рисовать на поверхности чужого окна, для чего изменим только что написанный код:

procedure TForm2.Button2Click(Sender: TObject);
var
   dc : HDC;
   Window : HWND;
begin
   Window := FindWindow ('TForm1', 'Form1');
   If Window <> 0 then begin
    dc := GetDC (Window);
    Rectangle (dc, 10, 10, 110, 110);
    ReleaseDC (Handle, dc);
   end
end;

Теперь во время работы приложения, если в системе зарегистрировано окно класса 'TForm1' с заголовком 'Form1', вывод будет осуществляться на него. Запустите параллельно откомпилированные модули минимального и только что созданного приложений. При щелчке на кнопке прямоугольник рисуется на поверхности чужого окна.
Замечу, что если закрыть Project1.exe и загрузить в Delphi соответствующий ему проект, при щелчке на кнопке прямоугольник будет рисоваться на поверхности окна формы, что будет выглядеть необычно.

Функции Windows для воспроизведения нуждаются в специальной величине типа HDC (Handle Device Context, ссылка на контекст воспроизведения), для задания значения которой необходимо иметь величину типа HWND - ссылка на окно, уникальный идентификатор всех зарегистрированных в системе окон. В зависимости от версии Delphi ссылки имеют тип либо Integer, либо LongWord.

Графическая система OpenGL, как и любое другое приложение Windows, также нуждается в ссылке на окно, на котором будет осуществляться воспроизведение - специальной ссылке на контекст воспроизведения - величина типа HGLRC (Handle openGL Rendering Context, ссылка на контекст воспроизведения OpenGL). Для получения этого контекста OpenGL нуждается в величине типа HDC (контекст воспроизведения) окна, на который будет осуществляться вывод.

Поэтому наши примеры имеют следующие строки в разделе private описания формы:

   DC: HDC;
   hrc: HGLRC;

А обработчик события OnCreate формы начинается со следующих строк:

  DC := GetDC(Handle);
   SetDCPixelFormat;
   hrc := wglCreateContext(DC);
   wglMakeCurrent(DC, hrc);

То есть мы получаем контекст воспроизведения Windows, задаем желаемый формат пикселей, создаем контекст воспроизведения OpenGL и делаем его текущим, чтобы вызываемые функции OpenGL могли работать с этим окном.

По поводу формата пикселей мы поговорим подробнее чуть позже, а сейчас я хотел бы обратить внимание на два момента.

Во-первых, величину типа HDC мы получаем при создании окна, в обработчике события OnCreate, или, другими словами, в обработчике сообщения WM_CREATE. Это является обычным и традиционном для Windows-программ.
Некоторые программисты сделали мне замечание, что получение контекста воспроизведения при создании окна является несколько некорректным для Windows 9X и более правильным было бы получение контекста в обработчике событий OnShow или OnPaint. Возможно, это так и есть, и в некоторых ситуациях может сказаться на корректности работы приложения. Вы должны учитывать это при написании ответственных приложений.

Во-вторых, контекст воспроизведения Windows и контекст воспроизведения OpenGL обычно освобождаются приложением. То есть, команды вывода OpenGL обычно обрамляются следующими строками:

   dc := BeginPaint(Window, ps); 
   wglMakeCurrent(DC, hrc);

   wglMakeCurrent(0, 0);
   EndPaint (Window,ps);
   ReleaseDC (Window, dc);

Повторяю, это обычные последовательности действий для Windows-программ, контекст воспроизведения должен быть доступен системе и другим приложениям. Я же во многих примерах пренебрегаю этим правилом для сокращения кода. Вы можете убедиться, что программы работают в общем случае корректно, хотя мы отдаем себе отчет, что в некоторых ситуациях такой подход может привести к "глюковатости" работы приложения. Это также надо учесть при написании ответственных приложений.

В наших примерах контекст воспроизведения OpenGL мы занимаем сразу же при его получении, в обработчике события OnCreate, а освобождаем в конце работы приложения, в обработчике события OnDestroy.

Еще одно замечание - команды и функции OpenGL имеют префикс gl для размещенных в библиотеке opengl32.dll и glu для размещенных в библиотеке glu32.dll. Прототипы этих функций находятся в модуле opengl.pas. Функции OpenGL, имеющие отношение только к реализации OpenGL под Windows, имеют префикс wgl, как, например, wglCreateContext, а некоторые вообще не имеют префикса, например, SwapBuffers. Их прототипы описаны в модуле windows.pas.


Количество кадровНаверх  

Иногда требуется узнать, сколько кадров выводит приложение за секунду, т.н. FPS (Frames Per Second). Вот один из способ, позволяющих это сделать:

Var
   NewCount, FrameCount, LastCount: LongInt; {счетчики кадров}
   FpsRate: GLFloat; {FPS}
...
newCount := GetTickCount; {получаем кол-во мс, прошедших с начала работы ОС}
 Inc(frameCount); {увеличение счетчика кадров после вывода кадра}
 If (newCount — lastCount) > 1000 then begin {прошла секунда}
 fpsRate := frameCount * 1000 / (newCount — lastCount);
 lastCount := newCount;
 frameCount := 0; 
end;

Количество выведенных за секунду кадров — в переменной fpsRate. На сегодня это все. Теперь вы сможете улучшить свои программы, сделать их более профессиональными с помощью предложенного программного арсенала средств. В следующей статье уже будет информация непосредственно по OpenGL.
Hosted by uCoz