Диаграмма управления функций модулей
Исходный текст контрольного модуля начинается следующими директивами подключения системных заголовочных файлов с логическими макрокодами клавиш X-графики и прикладного заголовка многоугольного графа:
#include <X11/keysym.h>
#include <X11/keysymdef.h>
#include "polyhedron.h"
Остальную часть контрольного модуля составляет исходный код его управляющих функций. Они рассмотрены ниже в топологическом порядке своих вызовов по диаграмме управления.
Наиболее простой в контрольном модуле является функция управления клавиатурой rekey. Эту функцию вызывает диспетчер событий dispatch для перезагрузки графического окна или завершения программы по нажатию управляющих клавиш ESCAPE и F10 на клавиатуре. При вызове ей передается адрес структуры события XEvent для распознавания логических кодов указанных клавиш. Его реализует клавиатурный запрос XKeycodeToKeysym логического кода клавиши (keysym) по ее физическому коду (keycode) в структуре полученного события. Требуемая обработка выбирается по логическим макрокодам XK_Escape и XK_F10, а любые другие значения игнорируются. В частности, XK_F10 означает завершение функции rekey с возвратом ненулевого значения (10) диспетчеру событий dispatch, которое прерывает цикл обработки событий. Другой допустимый логический макрокод XK_Escape вызывает функцию reset для перезагрузки графического окна. Вместе с дисплейными данными (Display и Window) из соответствующих полей структуры события ей передается признак FillFace закраски или очистки граней графа после перезагрузки. Его значение устанавливается по состоянию модифицирующей клавиши ALT, которая может быть нажата вместе с клавишей ESCAPE, (тогда FillFace=NFACE и происходит закраска граней) или свободная (FillFace=0) для ее отмены. Комбинация ALT-ESCAPE проверяется маскировкой флагом Mod1Mask поля состояния state в структуре адресованного события. В любом случае после перезагрузки происходит завершение функции rekey с нулевым возвратом в функцию диспетчеризации событий dispatch для продолжения цикла обработки событий. Рассмотренное клавиатурное управление реализует следующий исходный код функции rekey.
int rekey(XEvent* ev) { /* Обработка клавиатуры */
Display* dpy = ev->xkey.display; /* адрес дисплейный */
Window win = ev->xkey.window; /* идентификатор окна */
int FillFace; /* флаг очистки/закраски граней */
KeySym ks; /* логический код клавиши */
XKeycodeToKeysym(dpy, ev->xkey.keycode, 0);
if(ks == XK_F10) /* Контроль F10 для возврата кода */
return(10); /* завершения программы 10 */
FillFace = (ev->xkey.state & Mod1Mask) ? NFACE : 0;
if(ks == XK_Escape) /* Контроль Escape для очистки */
reset(dpy, win, FillFace); /* или перерисовки граней */
return(0); /* возврат кода 0 продолжения программы */
} /* rekey */
Главные управляющие действия контрольного модуля выполняет функция диспетчеризации событий dispatch. Ее вызывает основная функция main для управления обработкой событий, которые поступают в графическое окно программы из очереди Х-сервера. В соответствие с маской событий, которая была задана при создании окна, предусмотрена обработка событий следующих типов: перерисовка изображения (Expose), реконфигурация окна (ConfigureNotify), фокусировка ввода (FocusIn), нажатие любых кнопок мыши (ButtonPress) и клавиш клавиатуры (KeyPress). События перечисленных типов для окна программы читаются из очереди Х-сервера по запросу XNextEvent, заполняя структуру XEvent, и дифференцируются по значению ее поля типа в альтернативах цикла их обработки функциями дисплейного и геометрического модулей. Необходимые им параметры передаются через дисплейные аргументы (адрес дисплейной структуры Display, идентификаторы графического окна Window и графического контекста GC) функции dispatch и адресуются структурой XEvent полученных событий или ее отдельными, в частности, координатными и габаритными полями.
Основной целью обработки событий в функции dispatch является правильная раскраска граней заданного многоугольного графа, где любые смежные грани имеют различные цвета. Установку цвета каждой грани, выбранной курсором мыши по щелчку любой кнопки, обеспечивает обработка события ButtonPress функциями zotone и reface. При этом вызов функции zotone возвращает номер выбранной грани по координатам курсора мыши в соответствующих полях структуры полученного события и устанавливает очередное значение ее тонового индекса цвета в циклическом порядке. Вызов функции reface с дисплейными параметрами из аргументов функции dispatch и номером указанной грани обеспечивает перекраску внутренней области ее изображения в графическом окне установленным цветом.
Кроме раскраски граней, в программе предусмотрено масштабирование изображения графа при реконфигурациях его окна, которое обеспечивает обработка события ConfigureNotify функцией reconf. При вызове ей передаются текущие размеры графического окна в габаритных полях структуры полученного события. Если они отличаются от своих прежних значений, происходит координатная реконструкция геометрической модели графа для последующей перерисовки его изображения по событию типа Expose, которое автоматически возникает при любых реконфигурациях окна, когда изменяются его габариты (при передвижениях окна без изменения размеров событие Expose не возникает). Вообще событие Expose происходит при потере изображения в графическом окне по любой причине и обычно обрабатывается, чтобы восстановить изображение.
Для перерисовки графа в альтернативе обработки события Expose вызывается функция regraph. Ей передаются дисплейные параметры из аргументов функции dispatch и флаг закраски NoFillFace. Требуется задать NoFillFace=0 (False), чтобы производить закраску граней при перерисовке. Если NoFillFace=NFACE (True), перерисовывается только контур графа без раскраски граней в их цвета. Это позволяет уменьшить мерцание изображения в серийных перерисовках при интенсивных интерактивных изменениях габаритов окна в растровых оконных менеджерах GNOME или KDE. При каждом изменении размеров окна растровый менеджер стирает изображение в нем и требуется перерисовка. Для отмены перекрасок граней при промежуточных перерисовках нужно установить NoFillFace=NFACE. Эту установку обеспечивает возврат функции reconf при обработке каждого события габаритной реконфигурации графического окна.
Завершение серии реконфигураций графического окна KDE или GNOME индицирует событие фокусировки ввода FocusIn. Его обработка предусматривает установку флага NoFillFace=0 и вызов функции regraph для финальной перерисовки графа (или reset для перезагрузки окна WMaker и GNOME) с закраской граней в их цвета.
Следует отметить, что фреймовые оконные менеджеры типа OpenLook (olwm) или fvwm2 при реконфигурации окна манипулируют только его рамкой без серии промежуточных перерисовок изображения и гарантии события фокусировки ввода в конце. В этом случае нужно исключить контурные перерисовки графа, фиксируя NoFillFace=0 (False) в альтернативе обработки события Expose перед вызовом функции regraph, чтобы подавить возврат функции reconf по событию ConfigureNotify при изменениях размеров графического окна. Чтобы сократить объем коррекций в зависимости от типа оконного менеджера, достаточно снять или установить комментарий этой конструкции только для альтернативы Expose в исходном коде функции dispatch.
Цикл обработки рассмотренных событий должен продолжаться, пока признак его завершения done=0 (False). Выход из этого цикла происходит по значению done>0 (True), которое может быть установлено по событию KeyPress при нажатии клавиш клавиатуры. Его обработку обеспечивает управляющая функция rekey, в которую адресуется структура клавиатурного события, а ее код возврата определяет значение признака done. В зависимости от нажатой клавиши функция rekey осуществляет перезагрузку графического окна, сохраняя done=0, или возвращает в нее значение 10 для выхода из цикла обработки событий и завершения функции dispatch. Следующий исходный код формализует все ее альтернативы обработки событий.
/* Диспетчер событий */
int dispatch(Display* dpy, Window win, GC gc) {
int NoFillFace = 0; /* флаг закраски граней */
XEvent event; /* структура событий */
int done = 0; /* exit-код */
while(done == 0) { /* цикл обработки событий */
XNextEvent(dpy, &event); /* получить события */
switch(event.type) { /* выбор обработки события */
case Expose: /* Перерисовка графа */
if(event.xexpose.count > 0) break;
/* Снять комментарий для фреймовых WM (olwm) */
/* NoFillFace = 0; */
regraph(dpy, win, gc, NoFillFace);
break;
case ConfigureNotify: /* Реконфигурация графа */
NoFillFace = reconf(event.xconfigure.width,
event.xconfigure.height);
break;
case ButtonPress: /* Перекраска выбранной грани */
reface(dpy, win, gc,
zotone(event.xbutton.x, event.xbutton.y));
break;
case FocusIn: NoFillFace = 0; /* Для KDE и GNOME */
regraph(dpy, win, gc, NoFillFace);
/* снять комментарий для WMaker */
/* reset(dpy, win, NFACE); */
break;
case KeyPress: /* Обработка клавиатуры */
done = rekey(&event); /* break */
default: break;
} /* switch */
} /* while */
return(done);
} /* dispath */
Спецификацию модуля контроля завершает исходный код основной функции main, которой может передаваться название многогранника заданного графа необязательным аргументом командной строки вызова программы. Набор переменных основной функции main образуют дисплейные параметры (Display, Window, GC) и модельная структура полиграфа XPolyGraph. Кроме того, устанавливается титульный индекс для заголовка окна по аргументу командной строки или имени программы, когда она вызывается без аргумента. Процедурная часть функции main начинается установкой контакта с Х-сервером по запросу XOpenDisplay . Затем вызываются функции assoc и relink для адресации структуры графа в геометрический и дисплейный модули. После этого вызываются сервисные функции для распределения цветов (colorite), создания графических контекста (congraph) и окна (wingraph). Дальнейшее выполнение программы осуществляет диспетчерская функция dispatch в своем цикле обработки событий. При вызове ей передаются дисплейные переменные из функции main для функций обработки событий. После возврата из функции dispatch происходит корректное завершение программы по запросам XDestroyWindow и XCloseDisplay, чтобы закрыть ее окно и разорвать контакт с X-сервером. Все перечисленные вызовы и запросы составляют следующий исходный код основной функции main:
int main(int argc, char* argv[]) { /* Основная функция */
XPolyGraph heap; /* структура графа (пирамиды) */
Display* dpy; /* адрес дисплейной структуры */
Window win; /* идентификатор графического окна */
GC gc; /* графический контекст изображения */
int title = (argc > 1) ? 1 : 0; /* индекс заголовка окна */
dpy = XOpenDisplay(NULL); /* контакт с X-сервером */
assoc(&heap); /* модельная ассоциация графа */
relink(&heap); /* дисплейная адресация графа */
colorite(dpy); /* распределение палитры 5 цветов */
gc = congraph(dpy); /* настройка графического контекста */
win = canvas(dpy, tittle); /* создание именного окна */
dispatch(dpy, win, gc); /* диспетчеризация событий */
XDestroyWindow(dpy, win); /* закрыть окно */
XCloseDisplay(dpy); /* разрыв связи с X-сервером */
return(0); /* завершение программы */
} /* main */
Исходный код рассмотренных функциональных модулей программы раскраски графа следует сохранить в отдельных текстовых файлах, имена которых отражают название графа и номер модуля. В частности, модули программы раскраски графа пирамиды сохраняются в текстовых файлах pyramid[012].c в одном каталоге вместе с заголовочным файлом polyhedron.h и следующим make-файлом pyramid.m.
# Make-программа сборки проекта pyramid
EXE=pyramid # базовое имя exe-файла
CC=cc # компилирующая система
OBJ=$(EXE)0.o $(EXE)1.o $(EXE)2.o
all: $(OBJ) polyhedron.h
$(OBJ): polyhedron.h # неявная компиляция
$(EXE): $(OBJ) polyhedron.h # компоновка
$(CC) -o $(EXE) $(OBJ) –lX11
clean clear: # стирание объектных файлов
rm −f $(OBJ)
Спецификации этой make-программы обеспечивают автоматическую компиляцию исходного кода модулей и компоновку с графической библиотекой по следующей команде вызова интерпретатора make:
$ make -f pyramid.m
При этом выбор (под)целей и действий make-программы осуществляется по сравнению даты модификации исходных и объектных файлов, чтобы гарантировать получение и обновление выполняемого файла программы pyramid. Вызов его для выполнения в фоновом режиме (&) реализует следующая командная строка, аргумент HEAP которой задает заголовок окна:
$ pyramid HEAP &
При выполнении программы по этой команде необходимо построить правильную раскраску граней пирамиды минимальным числом цветов в графическом окне HEAP с оптимальным габаритным размером.
