|
Начнем наш разговор с понятий "событие" и "сообщение". Очень часто это синонимы одного и того же термина операционной системы, общающейся с приложениями посредством посылки сообщений. Код, написанный в проекте 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', вывод будет осуществляться на него.
Запустите параллельно откомпилированные модули минимального и только что
созданного приложений. При щелчке на кнопке прямоугольник рисуется на
поверхности чужого окна. Функции 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 и контекст воспроизведения 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. |