Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Общее устройство X Window.docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
79.21 Кб
Скачать

Общее устройство X Window

Оконно-сетевая графическая система X Windows System Массачусетским технологическим институтом, как универсальная операционная среда, которая обеспечивает разработку и реализацию переносимого графического интерфейса прикладных программ для различных графических станций, работающих под управлением различных версий OC UNIX, VAX/VMS, MS-DOS. Архитектура программного обеспечения этой системы основана на модели "сервер — клиентEБuot;. В терминологии WINDOWS System клиент (Х-клиент) это прикладная программа, которая выполняется в среде X WINDOWS System, используя её оконные, графические и сетевые ресурсы. Сервер (Х-сервер) это системная программа, которая обеспечивает взаимодействие клиентов с аппаратными средствами и доступ к ресурсам среды X WINDOWS System.

Концептуально, Х-сервер и Х-клиенты разделены коммуникационной средой и в общем случае могут располагаться на различных машинах, связанных в сеть и работать под управлением различных ОС. Это позволяет за терминалом одной машины работать с прикладной программой, которая выполняется на другой машине или была запущена с другого терминала . В простых случаях сервер и клиент функционируют на одной машине, используя многозадачные возможности ОС.

Установление связи между Х-сервером и клиентом происходит по по инициативе последнего. Х-сервер, не завершая работы с другими клиентами, открывает новый канал связи, размещая поступающие из канала запросы клиента в своей входной очереди. Запросы различных клиентов извлекаются из входной очереди, последовательно обрабатываются Х-сервером и реализуются на аппаратных средствах. В обратном направлении Х-сервер передаёт в каждый клиентский канал связи очередь событий, которые произошли на данном дисплее по аппаратным или программным причинам.

Взаимодействие Х-сервера и Х-клиента осуществляется в соответствии с Х-протоколом, который определяет формат запросов, передаваемых от клиента к серверу, и событий пересылаемых в обратном направлении. Что бы обеспечить разработчику клиентских программ интерфейс с распространённым языком программирования C, WINDOWS System предоставляет библиотеку функций Xlib.a, которая содержит около 300 модулей, которые кодируют все запросы Х-протокола и ответные реплики Х-сервера. Библиотека Xlib.a располагается в каталоге /usr/lib/X11 и должна быть присоединена к разрабатываемой прикладной программе на этапе редактирования связей. Различные макроопределения м макросы, используемые функциями данной библиотеки, сосредоточены в заголовочном файле Xlib.h, который располагается в каталоге /usr/include/X11. Модули клиентской программы должны включать файл Xlib.h с помощью директивы include.

# include <X11/Xlib.h>

Следует отметить, что X WINDOWS System предоставляет разработчику прикладных клиент программ инструментальные средства CXToolkit , InterViews, CLX(Common LispXInterface), которые обеспечивают интерфейс более высокого уровня, чем средства библиотеки Xlib.a и позволяют манипулировать объектами на языках C, C++, Lisp . Данное руководство содержит рекомендуемую стратегию разработки типичной клиентской программы.

Отображение текста

Пред отображением текстовой информации необходимо установить желаемый шрифт и цвет изображения, а также задать расположение текста в окне или пиксельной карте. Для установки шрифта рекомендуется использовать функцию XSetFont, которой в качетве параметра передаётся идентификатор шрифта , инициализируемый в поле fid структуры XFontStruct при загрузке при загрузке шрифта.

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

Что бы удачно задать расположение строки текстовой информации обычно достаточно оценить высоту символов шрифта и ширину строки. Высоту символов шрифта определяет сумма значений полей ascen и descent структуры XFontSruct. Ширину текстовой строки рекомендуется определять с помощью функции XTextWidth, передав ей в качестве аргумента адрес структуры параметров шрифта и адрес оцениваемой строки.

Непосредственно для изображения текстовой строки рекомендуется применять функцию XDrawImageString отображает в указанном окне заданное число литер адресуемой строки, используя установленный цвет символов и фона. Расположение текста определяется координатами его базовой линии, которые, как параметры передаются функции XDrawImage.

Следующий пример демонстрирует отображение заголовка окна с помощью загруженного шрифта, характеристики которого заданы в структуре bigfont:

static char head[]="ZAGOLOVOK OKNA";

XFontStruct *bigfont;

unsigned long colorcode[3];

Drawable win;

Display *dpy;

int x,y,w,h,win_width;

GC gc;

XSetFont(dpy,gc,bigfont->fid);

h=bigfont->ascent+bigfont->descent;

w=XTextWidth(bigfont,head,strlen(head));

y=3*h;

x=(win_width-w)/2;

XSetForeground(dpy,gc,colorcode[0]);

XSetBackground(dpy,gc,colorcode[1]);

XDrawImageString(dpy,win,gc,x,y,head,strlen(head));

Обработка событий

Любая клиентская программа должна обеспечивать обработку очереди событий, которые происходят на текущем дисплее и адресуются клиентам дисплея средствами Х-сервера. Номенклатура обрабатываемых событий для окон Х-клиента задаётся с помощью функции XSelectInput которой вместе с идентификатором окна передаётся маска типов событий, обрабатываемых в данном окне. Обычно рекомендуется обрабатывать следующие события: нажатие кнопок устройства, указателем мышь; нажатие клавиш клавиатуры; перемещение указателя мыши в окне; визуализация окна, после его перекрытия другими окнами. Что бы обеспечить обработку этих типов событий в заданном окне рекомендуется передать функции XSelectInput следующую маску:

ButtonPressMask|KeyPressMask|PointerMotionMask|ExposureMask

Обработку событий в клиентской программе рекомендуется поручить отдельному модулю, который будет осуществлять диспетчерские функции. Диспетчер событий должен обеспечивать цикличный приём событий от Х-сервера, анализ типа события и выполнение определённых действий по обработке событий каждого типа.

Для приёма событий каждого типа рекомендуется использовать функцию XNextEvent, которая возвращает полную спецификацию полученного события в структуре типа XEvent. При отсутствии событий в очереди функция XNextEvent переводит клиентскую программу в состояние ожидания следующего события. Рекомендуется обеспечить передачу адреса структуры событий XEvent всем модулям клиентской программы, которые связаны с обработкой событий.

Поле type структуры XEvent рекомендуется использовать для анализа типа события, применяя макросы ButtonPress, KeyPress, MotionNotify, Expose для идентификации типа события. Для обработки событий каждого окна рекомендуется использовать отдельные прикладные функции, передавая им адрес структуры XEvent в качестве аргумента.

Следующий фрагмент иллюстрирует диспетчера обработки событий в окне win, который прерывает цикл обработки событий после нажатия определённой клавиши на клавиатуре:

Display *dpy;

Window win;

unsigned long mask=(ButtonPressMask|KeyPressMask|PointerMotionMask|ExposureMask);

XEvent event;

int state=0;

XSelectInput(dpy,win,mask);

while(state==0) {

XNextEvent(dpy,win,&event);

switch(event.type) {

case Expose: f_expose(&event,...);

break;

case ButtonPress: f_button(&event,...);

break;

case KeyPress: f_key(&event,...);

break;

case MotionNotify: f_motion(&event,...);

break;

default: break;

}

}

Когда пользователь нажимает клавишу клавиатуры, программа получает событие KeyPress. Сервер также может послать событие KeyRelease, когда клавиша отпускается, но это справедливо не для всех типов компьютеров.

Оба этих события сопровождаются структурой типа XKeyEvent. Ее поле keycode содержит код нажатой клавиши, а поле state — состояние клавиш-модификаторов и кнопок мыши. Модификаторами называются такие клавиши, как Shift, Ctrl, Caps Look. Кроме этого, X предусматривает наличие дополнительных модификаторов, которые обозначаются Mod1, . . ., Mod5. Каждой нажатой клавише-модификатору и кнопке мыши соответствует флаг в поле state.

X Window System не останавливается на задании соответствия код клавиши — символ (ы), а идет дальше. Система позволяет программе сопоставить любой комбинации модификаторов и клавиш (например, <Shift+Ctrl+A>) ASCII строку (например, "EXIT"). Для некоторых клавиш соответствующие строки задаются сервером по умолчанию. Так символу XK_A соответствует строка "A".

Макрос XRebindKeysym() берет символ, список модификаторов и сопоставляет им строку.

Процедура XLookupString(), наоборот, берет событие о нажатии (отпускании) клавиши и возвращает соответствующие ему символ и строку. Последний ее параметр — указатель на структуру типа XComposeStatus. Дело в том, что некоторые клавиатуры имеют специальную клавишу Compose, которая позволяет печатать символы, которым нет соответствия среди клавиш. Так можно, например, набирать буквы разных алфавитов, такие как: 'й', 'ё' и т.д. Специальная таблица указывает, какой символ должен быть создан, если обычная клавиша нажимается одновременно с Compose. Ссылка на эту информацию и возвращается в структуре XComposeStatus.

"Захват" клавиатуры или мыши

Обычно фокус ввода может свободно переходить от окна к окну. Но иногда программе необходимо запретить передачу фокуса. Это называется "захватом" клавиатуры. Для того, чтобы его реализовать, используется процедура XGrabKeyboard().

Функция XGrabKey() запрещает передачу фокуса после нажатия определенной комбинации клавиш. Освободить клавиатуру можно, обратившись к процедуре XUngrabKeyboard( ) (XGrabKey( )).

Рассмотрим поведение системы при обработке событий от мыши. Как правило, если ее кнопка нажата в момент, когда ее курсор находится в неактивном окне, то последнее активизируется, и события от мыши передаются ему. Сказанное означает, что в нормальном состоянии окно получает только те события от мыши, которые соответствуют сигналам, пришедшим тогда, когда ее курсор находится в пределах окна. Но если программа вызывает

int XGrabPointer (Display* prDisplay, Window nGrabWnd,

Bool nOwnerEvents, unsigned int nEventMask,

int nPointerMode, int nKeyboardMode, Window nConfineTo,

Cursor nCursor, Time nTime);

то положение меняется. Теперь все события будут направляться окну с дескриптором nGrabWnd. "Освобождается" мышь вызовом XUngrabPointer( ). Процедура XGrabButton() указывает, что курсор должен быть "захвачен" после нажатия определенной кнопки. Обратной к ней является процедура XUngrabButton( ).

Процедуры, "захватывающие" устройство, — мышь или клавиатуру — имеют ряд аргументов, влияющих на поведение системы.

Так параметр nConfineTo есть идентификатор окна, за пределы которого не должен выходить курсор мыши, если он "захвачен".

Если аргумент nOwnerEvents равен Тrue, то события мыши будут передаваться окнам программы. Если nOwnerEvents — False, или курсор находится в окне, не принадлежащем программе, то события мыши передаются окну nGrabWnd.

Если nOwnerEvents равен False, то параметр nEventMask указывает, какие события следует передавать окну nGrabWnd.

Обработка событий от клавиатуры или ныши может быть приостановлена, если nPointerMode или nKeyboardMode равен GrabModeSync. В этом случае события буферизуются сервером, пока устройство не будет освобождено с помощью XUngrabKeyboard( ), XUngrabKey( ), XUngrabPointer( ) или XUngrabButton( ).

Параметр nCursor задает форму курсора во время того, как мышь "захвачена". Аргумент nTime указывает, когда система должна активизировать режим "захвата".

Разрыв связи с Х-сервером

Для корректного завершения клиентской программы в среде X Windows System рекомендуют освободить ресурсы, связанные с созданием окон, пиксельных карт, загрузкой шрифтов и прервать взаимодействие с Х-сервером для дисплея, специфицированного структурой Display в клиентской программе. Для реализации указанных действий рекомендуется применять функции XDestroyWindow, XFreePixMap, XFreeFont,XCloseDisplay.

Следующий пример представляет типичную концовку клиентской программы:

Display *dpy;

Window win;

Pixmap pix;

XFontStruct *font;

XFreeFont(dpy,font);

XFreePixmap(dpy,pix);

XDestroyWindow(dpy,win);

XCloseDisplay(dpy);

Структура клиентской программы

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

1. Инициализация взаимодействия с Х-сервером.

2. Создание окна (окон).

3. Загрузка шрифтов.

4. Распределение цветов.

5. Установка графического контекста.

6. Создание пиксельной карты.

7. Обработка событий.

8. Разрыв связи с Х-сервером.

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

Для удобства трансляции и редактирования связей модулей клиентской программы рекомендуется составить make-файл проекта. Для обозначения make-файла рекомендуется использовать базовое имя клиентской программы, добавив к нему расширение .m.

рис. 1 Структура интерфейса Xclient - Xserver

рис. 2 Структура X GUL

Инициализация взаимодействия с X-сервером

На первом шаге своего выполнения в среде X Windows System любая прикладная программа, использующая возможности этой системы для доступа к аппаратным средствам графической станции, должна установить взаимодействие с Х-сервером, сообщив имя дисплея, на котором она будет выполняться как клиент. Взаимодействие Х-клиента и Х-сервера устанавливается функцией XOpenDisplаy, которая возвращает указатель на структуру Display. Поля структуры Displayсодержат всю информацию об Х-сервере, необходимую клиентской программе. Адрес дисплея необходим любой функции библиотеки Xlib.a, которая включена в клиентскую программу. Поэтому рекомендуется обеспечит передачу адреса структуры Display соответствующим модулям клиентской программы. Нулевой возврат функции XOpenDisplay означает, что взаимодействие Х-клиента и Х-сервера установить не удалось и дальнейшее выполнение клиентской программы не имеет смысла.

Если существуют определённые предпочтения относительно дисплея, на котором должна выполняться клиентская программа, то имя дисплея в виде символьной строки определённого формата должно быть передано функции XOpenDisplay в качестве аргумента. Когда в качестве аргумента используется нулевой указатель NULL, то дисплейное имя устанавливается по значению переменной окружения DISPLAY, которая может быть изменена соответствующим образом командой setenv. Обычно значение переменной DISPLAY соответствует текущему дисплею.

Следующие примеры демонстрируют применение функции XOpenDisplay:

Display *dpy;

if((dpy=XOpenDisplay(NULL))==NULL){

fprintf(stderr,"Can't open display %s\n",XDisplayName(NULL));

exit(1);

}

Для последующих модулей клиентской программы часто необходима информация о номере экрана, на котором выполняется клиентская программа, его геометрических размеров и глубине. Эта информация может быть получена с помощью экранных макросов DefaultScreen, DefaultWidth DefaultHeigh и DefaultDept. Функция DefaultScreen имеет единственный аргумент — адрес структуры Display, специфицированный функции XOpenDisplay, и возвращает номер текущего экрана дисплея, на котором выполняется клиентская программа. Остальные экранные функции, кроме адреса структуры Display, используют в качестве аргумента номер экрана, специфицированный макросом DefaultScreen

Создание окна

Любая клиентская программа создаёт и использует одно или несколько окон, через которые осуществляется взаимодействие с пользователем. В простейшем случае клиент создаёт единственное окно, которое является потомком корневого окна X Windows System. Для создания окон обычно используется функция XCreateWindow.

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

Функция XCreateWindow имеет длинный список аргументов, который определяет геометрию, класс, визуальный тип, идентификатор родительского окна, маску и адрес структуры атрибутов создаваемого окна. Геометрические аргументы рекомендуется вычислять, исходя из параметров экрана, которые определяются с помощью экранных макросов.

Класс окна рекомендуется специфицировать константой InputOutput, которая гарантирует доступность операций как ввода, так и вывода для данного окна. Визуальный тип рекомендуется заимствовать у родительского окна, специфицировав длинный аргумент константой CopyFromParent. В качестве родительского окна рекомендуется выбрать корневое окно экрана, идентификатор которого специфицируется макросом DefaultRootWindow. Атрибуты окна задаются структурой XSetWindowAttributes(). В большинстве практических случаев существенными являются атрибуты цвета рамки, цвета фона и поддержки взаимодействия с оконным менеджером. Рекомендуется использовать цветовые макросы WhitePixel и BlackPixel для установки цвета фона и рамки и обеспечить поддержку менеджера окон, присвоив полю override_redirect структуры атрибутов значение False (или True, если поддержка менеджера окон не требуется в клиентской программе). Для того что бы активизировать назначенные значения атрибутов окна необходимо передать функции XCreateWindow адрес структуры атрибутов и маску статуса атрибутов, которая маскирует несущественные атрибуты. В рассмотренном случае рекомендуется использовать следующую маску:

CWBackPixel|CWBorderPixel|CWOverrideRedirect

Следующий пример демонстрирует создание окна, которое обладает перечисленными характеристиками, расположено в центре экрана и покрывает 1/9 площади экрана:

Display *dpy;

XSetWindowAttributes attr;

unsigned long mask=(CWBackPixel|CWBorderPixel|CWOverrideRedirect);

Window win;

int scr, x,y,w,h;

scr=DefaultScreen(dpy);

x=w=DefaultWidth(dpy,scr);

y=h=DefaultHeight(dpy,scr);

attr.border_pixel=BlackPixel(dpy,scr);

attr.background_pixel=WhitePixel(dpy,scr);

attr.override_redirect=False;

win=XCreateWindow(dpy,DefaultRootWindow(dpy),x,y,w,h,1,DefaultDepth(dpy,scr),

InputOutput,CopyFromParent,mask,&attr);

Следует отметить, что окно, созданное функцией XCreateWindow по умолчанию не является отображённым. Что бы обеспечить возможность отображения окна рекомендуется использовать функции XMapWindow, XMapRaised.

Если при создании окна был задан атрибут поддержки взаимодействия с оконным менеджером, то последнему необходимо сообщать текущие и предельно допустимые геометрические параметры окна, а также способ поддержки их изменения (программный или пользовательский). Характер взаимодействия клиента с менеджером окон определяется структурой XSizeHints. Рекомендуется присвоить соответствующие значения следующим полям структуры XSizeHints: х,у, width, height, min_width, min_height, max_width, max_height, flag и передать адреса структуры функции XSetNormalHints вместе с идентификатором окна. Что бы избежать возможных осложнений при конфигурировании окна с помощью менеджера окон рекомендуют полям:(width, min_width, max_width) и (height, min_height,max_height) присвоить одинаковые значения равные соответствующим геометрическим размерам созданного окна, а полям х,у — значения начальной позиции окна. На поле flag рекомендуется наложить маску:

(USPosition|USSize|PMinSize|PMaxSize),

которая обеспечивает возможность перемещения окна средствами менеджера окон и программное управление изменением его размеров (в данном случае неизменность).

Следующий пример иллюстрирует рассмотренное выше описанные взаимодействия окна с менеджером окон и является логическим продолжением примера создания окна.

HSizeHints hint;

hint.x=x;

hint.y=y;

hint.max_width=hint.min_width=hint.width=w;

hint.max_height=hint.min_height=hint.height=h;

hint.flag=(USPosition|USSize|PminSize|PMaxSize);

XSetNormalHints(dpy,win,&hint)

Загрузка шрифтов

Для вывода текста используются процедуры XDrawString(), XDrawImageString() и XDrawText(). Каждая из них имеет две версии. Первая используется для шрифтов, имеющих не более 256 символов. Если же символов больше ("большие" шрифты), то применяется вторая версия. Функции, работающие с "большими" шрифтами, имеют имена XDrawString16( ), XDrawImageString16( ) и XDrawText16( ). Параметры процедур, выводящих текст, задают дисплей, окно, графический контекст, строку, ее положение и т.д. Рисование идет в соответствии с областью отсечения контекста. Буквы или их части, находящиеся за пределами области отсечения, не изображаются. Наиболее часто употребляется процедура XDrawString() ( XDrawString16( ) ). Ее параметры дают строку, ее длину и положение в окне. Текст рисуется цветом переднего плана, выбранного в GC.

Функция XDrawImageString() ( XDrawImageString16( ) ) похожа на предыдущую процедуру с той лишь разницей, что фон символов при рисовании закрашивается цветом фона, установленного в GC. XDrawString( ) и XDrawImageString() выводят символы, используя шрифт, установленный в GC.

XDrawText() ( xDrawText16( ) ) позволяет рисовать несколько строк сразу, используя при этом разные шрифты. Каждая рисуемая единица задается структурой xTextItem.

Процедура XDrawText16( ) использует структуру XDrawText16.

Поле font, в приведенных структурах (XTextItem и XDrawText16) задает шрифт, используемый для рисования. Если значение поля font — None, то применяется шрифт, выбранный в GC.

Как мы уже говорили ранее, текст, как правило, рисуется шрифтом, выбранным в графическом контексте.

В растровых шрифтах каждому символу соответствует некоторый битовый шаблон, определяющий порядок закраски пикселов при рисовании. Если бит шаблона равен 1, то соответствующий элемент изображения закрашивается цветом переднего плана GC, если же он равен 0, то он закрашивается, либо цветом фона, либо вообще не рисуется.

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

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

Загрузка шрифта осуществляется процедурой XLoadFont(). Она берет в качестве аргумента имя шрифта, находит его и возвращает программе соответствующий идентификатор. Этот идентифи­Щтор передается затем процедуре XSetFont( ), чтобы выбрать шрифт в GC. Заметим, что реально шрифт с данным именем загружается сервером лишь один раз. После этого при обращениях к XLoadFont() с тем же именем шрифта, функция возвращает ссылку на шрифт, уже находящийся в памяти компьютера.

По умолчанию X ищет файл со шрифтом в директории "usr/lib/X11/fonts". Программист может задать дополнительные директории для поиска с помощью процедуры XSetFontPath( ).

Имя шрифта в X начинается с "-" и состоит из двух частей. Между ними стоит "--". В свою очередь, каждая из частей состоит из полей — слов, разделенных "-".

В первой части указывается следующее:

- изготовитель шрифтам (foundry), например adobe;

- семейство шрифта (font family), например courier, helvetica;

- жирность шрифта (weight), например bold;

- наклон шрифта (slant);

- ширина букв шрифта (width)

Во второй части указывается следующее:

- размер шрифта в пикселах (pixels);

- размер шрифта в десятых долях "точки" ("точка" равна 1/72 дюйма );

- горизонтальное разрешение устройства, для которого разработан шрифт (horizontal resolution in dpi); величина измеряется в числе "точек" на дюйм;

- вертикальное разрешение устройства, для которого разработан шрифт (vertical resolution in dpi); величина измеряется в числе "точек" на дюйм;

- тип шрифта (spacing); возможные значения параметра следующие: m — шрифт с фиксированной шириной символов; p — пропорциональный шрифт с переменной шириной символов;

- средняя ширина символов шрифта, изморенная в десятых долях пиксела (average width);

- множество символов шрифта в кодировке ISO (International Standards Organisation) (character set).

Ниже приведен пример названия шрифта.

-adobe-courier-bold-o-normal--10-100-75-75-m-60-iso8859-1

Части имени могут заменяться символом "*" или "?". В этом случае X Window System подбирает шрифт, сличая имена имеющихся шрифтов с предоставленным шаблоном, так, как это делается при поиске файлов в UNIX. Например, шаблону

*charter-medium-i-*-240-*

соответствуют имена

-hit-charter-medium-i-normal-25-240-75-75-p-136-iso8859-1

-hit-charter-medium-i-normal-33-240-100-75-p-136-iso8859-1

Названия шрифтов, доступных в системе, хранятся в соответствующей базе данных. Получить список имен шрифтов можно с помощью процедуры XlistFonts() или XListFontsWithInfo(). Список шрифтов, возвращаемый этими функциями, должен быть освобожден вызовом XFreeFontNames().

Некоторые шрифты, такие как "fixed" или "9x15", доступны всегда.

Получить информацию о загруженном шрифте можно с помощью функции XQueryFont(), которая возвращает заполненную структуру типа XFontInfo(). Одновременно загрузить шрифт и получить информацию о нем можно с помощью процедуры XLoadQueryFont().

Когда информация о шрифте больше не нужна, ее следует освободить с помощью XFreeFontInfo(). Когда становится не нужен и сам шрифт, последний надо "сбросить", обратившись к процедуре XUnloadFont(). Функция XFreeFont() объединяет в себе XFreeFontInfo() и XUnloadFont().

Следующий фрагмент кода загружает шрифт "Courier", создает GC и выводит с его помощью строку "Hellow, world!".

Display *prDisplay;

GC prGC;

Window nWnd;

XFontStruct *prFontInfo;

. . . . . . .

/* Загружаем шрифт */

if ( (prFontInfo=

XLoadQueryFont(prDisplay, "*-courier-*" )) == NULL){

printf("Font not found!\n");

exit(1);

}

. . . . . . .

/* Создаем GC и рисуем строку */

prGC=XCreateGC(prDisplay, nWnd, 0, NULL);

XSetForeground (prDisplay, prGC, BlackPixel(prDisplay, 0));

XSetFont (prDisplay, prGC, prFontInfo->fid);

XDrawString (prDisplay, nWnd, prGC, 10, 50, "Hello, world!",

strlen ("Hello, world!") );

XFreeGC (prDisplay, prGC);

. . . . . . .

/* "Сбрасываем" шрифт */

XUnloadFont (prDisplay, prFontInfo->fid);

.......................

Следующий пример демонстрирует загрузку двух шрифтов:

static char bigfont[]="9x15";

static char littlefont[]="6x13";

XFontStruct *bf;

XFontStruct *lf;

Display *dpy;

if ((bf=XLoadQueryFont(dpy,bigfont)==0)

{

fprintf (stderr,"Can't load font%s\n",bigfont);

exit(1);

}

if ((lf=XLoadQueryFont(dpy,littlefont)==0)

{

fprintf (stderr,"Can't load font%s\n",littlefont);

exit(1);

}

Распределение цветов

Клиентская программа может использовать несколько различных цветов для отображения примитивов графического вывода. С целью спецификации цветов в клиентской программе используется цветовая карта Colormap, в ячейки которой распределяются данные по необходимым цветам. Рекомендуется использовать стандартную карту цветов, ассоциировав её с окном клиента с помощью макроса DefaultColormap.

Количество ячеек цветовой карты, определяющее степень допустимого разнообразия цветов экспоненциально зависит от числа от числа цветовых плоскостей экрана. Рекомендуется использовать макрос DefaultDepth для оценки цветовых возможностей экрана. Например, монохромный экран имеет только одну цветовую плоскость и, следовательно, в цветовую карту могут быть распределены только два цвета, коды которых могут быть легко получены с помощью макросов BlackPixel и WhitePixel. Настройка цветов в монохромном варианте не является необходимой.

Для цветных экранов цветовую настройку клиентской программы рекомендуется выполнять в 3 этапа:

1. Поиск нужного цвета по имени в базе цветов Х-сервера с функции XLookUpColor и сохранение цветовых данных в структуре XColor. База цветов Х-сервера находится в файле /usr/lib/X11/rgb. ASCII версия базы цветов расположена в файле /usr/lib/X11/rgb.txt, который рекомендуется использовать для выбора необходимых цветов по названию.

2. Распределение цветовых данных в ячейках цветовой карты с помощью функции XAllocColor.

3. Для однозначной идентификации цвета обычно используется поле pixel структуры цветовых данных XColor, которое содержит значение пиксельного кода цвета. Пиксельные коды распределённых цветов рекомендуется сохранить в специальном массиве с элементами типа unsigned long и обеспечить его передачу модулям клиентской программы, которые реализуют цветной графический вывод. Нумерация элементтов массива кодов цветов должна соответствовать порядку их распределения в цветовой карте.

Элементы массива пиксельных кодов полезно инициализировать с помощью макросов BlackPixel и WhitePixel в зависимости от тонального цвета, распределённого в соответствующую ячейку цветовой карты. Предварительная инициализация позволяет избежать конфликтных ситуаций, если по каким-либо причинам невозможно корректно распределить выбранный цвет в цветовую карту.

Пример распределения трёх цветов:

Display *dpy;

int scr;

Colormap map;

XColor soft, hard;

int i=0;

static char * colorname[]= {

"red",

"yellow",

"blue",

NULL

};

unsigned long codecolor[4];

while(i<4)

codecolor[i++]=BlackPixel(dpy,scr);

if(DefaultDepth(dpy,scr)==1)

return(1);

map=DefaultColormap(dpy,scr);

for(i=0;i<4;i++) {

if(XLookupColor(dpy,map,colorname[i],&soft,&hard)==0)

continue;

if(XAllocColor(dpy,map,&soft)==0)

continue;

colorcode[i]=soft.pixel;

}

Установка графического контекста

Прежде чем начать работу с графикой, программа должна выделить себе специальную структуру данных и получить указатель на нее. Эта структура называется графическим контекстом (Graphic Context (GC)). Указатель на GC используется в качестве одного из параметров при вызове "рисующих" функций X Window System. Графический контекст содержит ряд атрибутов, влияющих на изображение объектов: текста, линий, фигур и др. Выделенный GC должен быть освобожден до завершения работы программы.

Графический контекст создается процедурой XCreateGC(), имеющей следующий прототип:

GC XCreateGC (Display *prDisplay, Drawable nDrawable, . unsigned long nValueMask, XGCValues *prValues);

Первый аргумент — это указатель на структуру типа Display, который программа получает после вызова XOpenDisplay(); второй — идентификатор окна (или карты пикселов), в котором программа будет рисовать; третий — битовая маска, определяющая, какие атрибуты GC задаются; последний аргумент — структура типа XGCValues, определяемая следующим образом:

typedef struct {

int function;

unsigned long plane_mask;

unsigned long foreground;

unsigned long background;

int line_width;

int line_style;

int cap_style;

int join_style;

int fill_style;

int fill_rule;

int arc_mode;

Pixmap tile;

Pixmap stipple;

int ts_x_origin;

int ts_y_origin;

Font font;

int subwindow_mode;

Bool graphics_exposures;

int clip_x_origin;

int clip_y_origin;

Pixmap clip_mask;

int dash_offset;

char dashes;

} XGCValues;

Значения полей данной структуры будут объяснены ниже. Каждому из них соответствует бит в маске, которая передается в качестве третьего параметра при вызове процедуры XCreateGC(). Эти биты обозначаются символическими константами, определенными в файле "Xlib.h". Если бит установлен, то значение соответствующего атрибута должно быть взято из переданной XCreateGC() структуры XGCValues. Если бит сброшен, то атрибут приникает значение по умолчанию.

Следующий пример показывает процесс создания графического контекста, в котором устанавливаются два атрибута: цвет фона и цвет переднего плана.

. . . . . . .

GC prGC;

XGCValues rValues;

Display prDisplay;

int nScreenNum;

. . . . . . . .

rValues.foreground = BlackPixel (prDisplay, nScreenNum);

rValues.background = WhitePixel (prDisplay, nScreenNum);

. . . . . . . .

prGC = XCreateGC (prDisplay, RootWindow (prDisplay, nScreenNum),

(GCForeground | GCBackground), &rValues);

Вызов XCreateGC() не единственный способ создания графического контекста. Так, например, новый контекст может быть получен из уже существующего GC с помощью XCopyGC().

Когда контекст порожден, его атрибуты могут изменяться процедурой XChangeGC(). Например:

rValues.line_width = 10;

XChangeGC (prDisplay, prGC, GCLineWidth, &rValues);

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

Для того, чтобы получить значение полей GC, используется процедура XGetGCValues().

Мы уже говорили, что GC имеет ряд атрибутов, воздействующих на вывод изображений. Для текста это цвет и шрифт, для линий — цвет и толщина и т.д. Как уже упоминалось выше, атрибуты контекста задаются в момент его создания. Потом они могут меняться с помощью функции XChangeGC(). Кроме того, X Window поддерживает специальные функции для изменения параметров GC.

Ниже перечисляются основные характеристики графического контекста и процедуры, меняющие их.

Режим рисования (поле function в структуре XGCValues) указывает, каким образом комбинируются при рисовании цвет графики и цвет изображения, на которое накладывается графика. Данное поле задает некоторую логическую функцию.

По умолчанию function равно GXcopy. Устанавливается режим рисования с помощью процедуры XSetFunction( ).

Изменяемые цветовые плоскости. Каждый пиксел задается с помощью N бит. Биты с одним номером во всех пикселах образуют как-бы плоскости, идущие параллельно экрану. Получить число плоскостей для конкретного дисплея можно с помощью макроса DisplayPlanes(). Поле plane_mask структуры графического контекста определяет, в каких плоскостях идет рисование при вызове функций X Window. Если бит поля установлен, то при рисовании соответствующая плоскость изменяется, в противном случае она не затрагивается.

Цвет переднего плана и фона (поля foreground и background) задают цвета, используемые при рисовании линий текста и других графических элементов. Устанавливаются значения указанных полей функциями XSetForeground() и XSetBackground() соответственно.

Атрибуты, влияющие на рисование линий. Шесть параметров определяют вид прямых, дуг и многоугольников, изображаемых с помощью X Window.

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

Поле line_style определяет тип линии. Возможные значения следующие:

LineSolid — сплошная линия,

LineOnOffDash — пунктирная линия; промежутки между штрихами не закрашиваются;

LineDoubleDash — пунктирная линия; промежутки между штрихами закрашиваются цветом фона;

Параметр cap_style определяет вид линии в крайних точках, если ее ширина больше 1 пиксела.

Поле join_style определяет, как соединяются линии друг с другом. Параметр имеет смысл при толщине линии большей 1.

Параметр dash_offset указывает, с какого места начинать рисование 1-й черточки пунктирной линии.

Для установки параметров линии используется процедура XSetLineAttributes().

Поле font определяет шрифт, используемый для вывода текста. Задать этот параметр можно с помощью процедуры XSetFont( ).

Процесс рисования включает в себя два этапа. На первом определяются пикселы, которые должны быть закрашены. После этого цвет выделенных точек изменяется. Так, для линии входящие в нее пикселы определяются по специальному алгоритму, а потом закрашиваются, например, цветом переднего плана.

Способ закраски определяется полем fill_style. Он устанавливается процедурой XSetFillStyle( ) и воздействует на все функции, рисующие линии, текст и фигуры. Исключение составляет случай, когда выводится линия, для которой значение line_width равно 0. Возможные значения параметра fill_style перечислены ниже.

FillSolid — для закраски используются цвета переднего плана и фона.

FillTiled — для закраски используется карта пикселов, определяемая параметром tile графического контекста; при этом карта как-бы располагается в окне так, что ее левый верхний угол имеет координаты ts_x_origin и ts_y_origin; затем определяется ее пересечение с рисуемой графикой, и пикселы, попавшие в пересечение, закрашиваются; значения полей ts_x_origin, ts_y_origin устанавливаются процедурой XSetTSOrigin( ); карта tile должна иметь ту же "толщину" (число бит-на-пиксел), что и окно, в котором производится рисование.

FillStippled — для закраски используется карта пикселов, задаваемая полем stipple; данная карта должна иметь "толщину" в 1 бит; способ закраски такой же, как и в случае FillTiled с той лишь разницей, что рисуются лишь те пикселы графики, которым соответствует установленный бит в карте stipple; цвет пиксела задается полем foreground.

FillOpaqueStippled — аналогично значению FillStippled, только пикселы, для которых не установлен бит в карте stipple, закрашиваются цветом фона.

Для задания полей tile и stipple можно использовать карты любого размера. На некоторых устройствах при определенных размерах рисование идет намного быстрее. Для получения таких размеров можно использовать процедуры XQueryBestSize( ), XQueryBestStipple( ), XQueryBestTile( ).

Режим заполнения многоугольников указывает, как заполнять цветом многоугольники, стороны которых пересекаются. Возможные значения следующие:

EvenOddRule — заполняются точки фигуры, определяемые по следующему правилу: пусть для некоторой линии растра n1, n2, . . . , nk — стороны многоугольника, которые ее пересекают; тогда закрашиваются точки между n1 и n2, n3 и n4, и т.д.

WindingRule — заполняется вся внутренность фигуры.

Режим заполнения дуг (поле arc_mode). Параметр задается процедурой XSetArcMode( ) и влияет на вид фигур, рисуемых процедурами XFillArc( ) и XFillArcs( ). В

Влияние подокон на рисование графических примитивов определяется полем subwindow_mode. Оно устанавливается процедурой XSetSubwindowMode( ) и имеет следующие значения:

ClipByChildren — часть графики, перекрываемая подокнами, не видна;