Назад |
Продолжая традиции многих изданий, посвященных программированию на С, мы начинаем с программы, рисующей на экране строку "Hello, world!"'. В этом примере приведены основные шаги, необходимые для работы в X Window. ( xhello.tgz)
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <stdio.h>
#include <string.h>
#define WND_X 0
#define WND_Y 0
#define WND_WDT 100
#define WND_HGH 100
#define WND_MIN_WDT 50
#define WND_MIN_HGH 50
#define WND_BORDER_WDT 5
#define WND_TITLE "Hello!"
#define WND_ICON_TITLE "Hello!"
#define PRG_CLASS "Hello!"
/*
* SetWindowManagerHints - процедура передает информацию о
* свойствах программы менеджеру окон.
*/
static void SetWindowManagerHints (
Display * prDisplay, /*Указатель на структуру Display */
char * psPrgClass, /*Класс программы */
char * argv[], /*Аргументы программы */
int argc, /*Число аргументов */
Window nWnd, /*Идентификатор окна */
int x, /*Координаты левого верхнего */
int y, /*угла окна */
int nWidth,
int nHeight, /*Ширина и высота окна */
int nMinWidth,
int nMinHeight, /*Минимальные ширина и высота окна */
char * psTitle, /*Заголовок окна */
char * psIconTitle,/*Заголовок пиктограммы окна */
Pixmap nIconPixmap /*Рисунок пиктограммы */
)
{
XSizeHints rSizeHints; /*Рекомендации о размерах окна*/
#ifdef X11R3 /*. X11R3 и ниже */
rSizeHints.flags = PPosition | PSize | PMinSize;
rSizeHints.x = x;
rSizeHints.y = y;
rSizeHints.width = nWidth;
rSizeHints.height = nHeight;
rSizeHints.min_width = nMinWidth;
rSizeHints.min_height = nMinHeight;
XSetStandardProperties ( prDisplay, nWnd, psTitle,
psIconTitle, nIconPixmap, argv, argc, &rSizeHints );
#else /* X11R4 и выше */
XWMHints rWMHints;
XClassHint rClassHint;
XTextProperty prWindowName, prIconName;
if ( !XStringListToTextProperty (&psTitle, 1, &prWindowName ) ||
!XStringListToTextProperty (&psIconTitle, 1, &prIconName ) ) {
puts ( "No memory!\n");
exit ( 1 );
}
rSizeHints.flags = PPosition | PSize | PMinSize;
rSizeHints.min_width = nMinWidth;
rSizeHints.min_height = nMinHeight;
rWMHints.flags = StateHint | IconPixmapHint |
InputHint;
rWMHints.initial_state = NormalState;
rWMHints.input = True;
rWMHints.icon_pixmap= nIconPixmap;
rClassHint.res_name = argv[0];
rClassHint.res_class = psPrgClass;
XSetWMProperties ( prDisplay, nWnd, &prWindowName,
&prIconName, argv, argc, &rSizeHints, &rWMHints,
&rClassHint );
#endif
}
/*
*main - основная процедура программы
*/
void main ( int argc, char * argv[] )
{
Display *prDisplay; /* Указатель на структуру Display */
int nScreenNum; /* Номер экрана */
GC prGC;
XEvent rEvent;
Window nWnd;
/* Устанавливаем связь с сервером */
if ( ( prDisplay = XOpenDisplay ( NULL ) ) == NULL ) {
puts ("Can not connect to the X server!\n");
exit ( 1 );
}
/* Получаем номер основного экрана */
nScreenNum = DefaultScreen ( prDisplay );
/* Создаем окно */
nWnd = XCreateSimpleWindow ( prDisplay,
RootWindow ( prDisplay, nScreenNum ),
WND_X, WND_Y, WND_WDT, WND_HGH, WND_BORDER_WDT,
BlackPixel ( prDisplay, nScreenNum ),
WhitePixel ( prDisplay, nScreenNum ) );
/* Задаем рекомендации для менеджера окон */
SetWindowManagerHints ( prDisplay, PRG_CLASS, argv, argc,
nWnd, WND_X, WND_Y, WND_WDT, WND_HGH, WND_MIN_WDT,
WND_MIN_HGH, WND_TITLE, WND_ICON_TITLE, 0 );
/* Выбираем события, обрабатываемые программой */
XSelectInput ( prDisplay, nWnd, ExposureMask | KeyPressMask );
/* Показываем окно */
XMapWindow ( prDisplay, nWnd );
/* Цикл получения и обработки ошибок */
while ( 1 ) {
XNextEvent ( prDisplay, &rEvent );
switch ( rEvent.type ) {
case Expose :
/* Запрос на перерисовку */
if ( rEvent.xexpose.count != 0 )
break;
prGC = XCreateGC ( prDisplay, nWnd, 0 , NULL );
XSetForeground ( prDisplay, prGC,
BlackPixel ( prDisplay, 0) );
XDrawString ( prDisplay, nWnd, prGC, 10, 50,
"Hello, world!", strlen ( "Hello, world!" ) );
XFreeGC ( prDisplay, prGC );
break;
case KeyPress :
/* Нажатие клавиши клавиатуры */
XCloseDisplay ( prDisplay );
exit ( 0 );
}
}
}
Для сборки программы используется команда:
cc -o hello hello.o -lX11
Программа использует ряд функций, предоставляемых библиотекой Xlib: XOpenDisplay( ), XCreateSimpleWindow( ) и др. Их прототипы, стандартные структуры данных, макросы и константы описаны в следующих основных файлах-заголовках: "Xlib.h", "Xutil.h", "Xos.h". Эти и другие файлы поставляются вместе с X Window.
Для обозначения переменных в книге принята нотация, пришедшая из Microsoft Windows. Идентификатор начинается с префикса, описывающего тип переменной, за которым следует ее имя. Ниже перечислены наиболее распространенные префиксы.
c - символ (байт) ,
n - байт, целое,
s - строка,
p - указатель,
r - структура.
Перейдем к рассмотрению самой программы. Она начинается установлением связи с Х-сервером. Делает это функция XOpenDisplay( ). Ее аргумент определяет сервер, с которым надо связаться. Если в качестве параметра XOpenDisplay( ) получает NULL, то она открывает доступ к серверу, который задается переменной среды (environment) DISPLAY. И значение этой переменной и значение параметра функции имеют следующий формат: host:server.screen, где host - имя компьютера, на котором выполняется сервер, server - номер сервера (обычно это 0), а screen - это номер экрана. Например, запись kiev:0.0 задает компьютер - "kiev", а в качестве номера сервера и экрана используется 0. Заметим, что номер экрана указывать не обязательно.
Процедура XOpenDisplay() возвращает указатель на структуру типа DISPLAY. Это большой набор данных, содержащий информацию о сервере и экранах. Указатель следует запомнить, т.к. он используется в качестве параметра во многих процедурах Xlib.
После того, как связь с сервером установлена, программа "Hello" определяет номер экрана. Для этого используется макрос DefaultScreen(), возвращающий номер основного экрана. Переменная nScreenNum может иметь значение от 0 до величины (ScreenCount (prDisplay ) - 1). Макрос ScreenCoun() позволяет получить число экранов, обслуживаемых сервером.
Следующий шаг - создание окна и показ его на дисплее. Для этого программа обращается к процедуре XCreateWindow() или XCreateSimpleWindow(). Для простоты мы используем вторую процедуру, параметры которой задают характеристики окна.
PrWind = XCreateSimpleWindow (
prDisplay, /* указатель на структуру Display,
описывающую сервер */
RootWindow (prDisplay, nScreenNum),
/* родительское окно, в данном случае,
это основное окно программы */
WND_X, WND_Y,
/* начальные x и y координаты верхнего
левого угла окна программы */
WND_WIDTH, WND_HEIGHT,
/* ширина окна и высота окна */
WND_BORDER_WIDTH, /* ширина края окна */
BlackPixel ( prDisplay, nScreenNum ),
/* цвет переднего плана окна */
WhitePixel ( prDisplay, nScreenNum )
/* цвет фона окна */
);
Для задания цветов окна используются макросы BlackPixel() и WhitePixel(). Они возвращают значения пикселов, которые считаются на данном дисплее и экране соответствующими "черному" и "белому" цветам. Функция XCreateSimpleWindow() ( XCreateWindow() ) возвращает значение типа Window. Это целое число, идентифицирующее созданное окно.
Среди параметров функций, создающих окна, есть те, которые определяют положение окна и его размеры. Эти аргументы принимаются во внимание системой X Window. Исключение составляет случай, когда родительским для создаваемого окна является "корневое" окно экрана. В этом случае решение о положение окна и его размерах принимает менеджер окон. Программа может пытаться повлиять на решение менеджера окон, сообщив ему свои "пожелания" с помощью функций XSetStandardProperties() и XSetWMHints() (для X версии 11.3 и ниже) или XSetWMProperties() (для X версии 11.4 и выше).
Из листинга 2.1 видно, что программа может сообщить менеджеру следующие параметры:
Имя окна и имя пиктограммы в X11R3 и ниже передаются как строки, через параметры функции XSetStandardProperties(). В X11R4 и выше строки должны быть в начале преобразованы в "текстовые свойства", описываемые структурами типа XTextProperty. Это выполняется процедурой XStringListToTextProperty().
Для передачи информации о желаемой геометрии окна используется структура XSizeHints().
X Window версии 11.4 (и выше) позволяет сообщить менеджеру также следующее:
После того, как "рекомендации" менеджеру окон переданы, программа выбирает события, на которые она будет реагировать. Для этого вызывается функция XSelectInput(). Ее последний аргумент есть комбинация битовых масок (флагов). В нашем случае - это ExposureMask | KeyPressMask. ExposureMask сообщает X Window, что программа обрабатывает событие Expose. Оно посылается сервером каждый раз, когда окно должно быть перерисовано. KeyPressMask выбирает событие KeyPress - нажатие клавиши клавиатуры. Типы событий и соответствующие им маски и структуры данных описаны в приложении 1.
Теперь окно программы создано, но не показано на экране. Чтобы это произошло, надо вызвать процедуру XMapWindow(). Заметим, что из-за буферизации событий библиотекой Xlib, окно не будет реально нарисовано, пока программа не обратится к процедуре получения сообщений от сервера ( XNextEvent() ).
Программы для X построены по принципу управляемости событиями. Поэтому, после того, как окно создано, заданы необходимые параметры для менеджера окон, основная ее работа - это получать сообщения от сервера и откликаться на них. Выполняется это в бесконечном цикле. Очередное событие "вынимается" процедурой XNextEvent(). Само оно есть переменная типа XEvent, который представляет собой объединение (union) структур. Каждое событие (Expose, KeyPress и т.д.) имеет свои данные (и, следовательно, свое поле в объединении XEvent) Более подробно они описаны в приложении 1.
При получении сообщения Expose, программа перерисовывает окно. Действия начинаются созданием графического контекста - структуры, которая содержит данные, необходимые для вывода информации, в нашем случае - текста:
prGC = XCreateGC (prDisplay, prWnd, 0, NULL);
После этого рисуется строка "Hello, world!". Более графический контекст не нужен - он уничтожается:
XFreeGC (prDisplay, prGC);
Окно может получить несколько событий Expose одновременно. Чтобы не перерисовывать себя многократно, программа дожидается прихода последнего из них и только потом осуществляет вывод.
Приход события KeyPress означает, что программу надо завершить: прекратить связь с сервером
XCloseDisplay (prDisplay);
и вызвать функцию exit().