: Программирование в среде X Window на основе библиотеки Xlib :

С о д е р ж а н и е

Введение

I. Базовые понятия X Window
Характеристики X-окна
Менеджер окон
Свойства и атомы
Первый пример
События
Атрибуты окна
Операции над окнами

II. Текст и графика
Графический контекст
Характеристики графического контекста
Вывод текста, работа с шрифтами
Использование цвета
Битовые и пиксельные карты
Изменяем курсор мыши

III. Работа с внешними устройствами
Клавиатура
Мышь

IV. Ресурсы программ
Формат файла ресурсов
Доступ к ресурсам программ

V. Взаимодействие клиентов
Механизм свойств
Работаем с менеджером окон

VI. Дополнительная информация

VII. Литература

Первый пример

Начнем изучение с программы, рисующей на экране строку, например "First, example!". Для начала рассмотрим основные шаги, необходимые для работы в X Window. Этот пример можно в дальнейшем можно использовать как шаблон для ваших собственных более сложных программ.

Программа должна выполнить следующие действия:

  1. Установить соедининение клиента с X сервером при помощи функции XOpenDisplay(), в случае неудачи выход с сообщением об ошибке.
  2. Получить информацию о физическом экране, это можно использовать, чтобы вычислить желаемый размер окна.
  3. Создать окно с помощью функции XCreateSimpleWindow().
  4. Установить свойства для менеджера окна.
  5. Выбрать тип события, который будет обрабатываться XSelectInput().
  6. Создать графический контекст, чтобы управлять запросами на прорисовку.
  7. Показать окно с помощью функции XMapWindow().
  8. Бесконечный цикл для приёма событий
  9. Создать запрос на перерисовку Expose, также здесь описываются действия программы по умолчанию.
  10. Добавить другие события, KeyPress, ButtonPress, или другие.
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <stdio.h>
#include <string.h>

#define X 0
#define Y 0
#define WIDTH 200
#define HEIGHT 200
#define WIDTH_MIN 50
#define HEIGHT_MIN 50
#define BORDER_WIDTH 5
#define TITLE "Example"
#define ICON_TITLE "Example"
#define PRG_CLASS "Example"

/*
* SetWindowManagerHints - функция, которая передает информацию о
* свойствах программы менеджеру окон. 
*/

static void SetWindowManagerHints ( 
 Display * display, 		/*Указатель на структуру Display */
 char * PClass, 		/*Класс программы */
 char * argv[],   		/*Аргументы программы */
 int argc,    			/*Число аргументов */
 Window window,    		/*Идентификатор окна */
 int x,     			/*Координаты левого верхнего */
 int y,	   				/*угла окна */
 int win_wdt,			/*Ширина  окна */
 int win_hgt,  			/*Высота окна */
 int win_wdt_min,			/*Минимальная ширина окна */
 int win_hgt_min, 		/*Минимальная высота окна */
 char * ptrTitle,  		/*Заголовок окна */
 char * ptrITitle,	/*Заголовок пиктограммы окна */
 Pixmap pixmap 	/*Рисунок пиктограммы */
)
{
 XSizeHints size_hints; /*Рекомендации о размерах окна*/

 XWMHints wm_hints;
 XClassHint class_hint;
 XTextProperty windowname, iconname;

 if ( !XStringListToTextProperty (&ptrTitle, 1, &windowname ) ||
    !XStringListToTextProperty (&ptrITitle, 1, &iconname ) ) {
  puts ( "No memory!\n");
  exit ( 1 );
}

size_hints.flags = PPosition | PSize | PMinSize;
size_hints.min_width = win_wdt_min;
size_hints.min_height = win_hgt_min;
wm_hints.flags = StateHint | IconPixmapHint | InputHint;
wm_hints.initial_state = NormalState;
wm_hints.input = True;
wm_hints.icon_pixmap= pixmap;
class_hint.res_name = argv[0];
class_hint.res_class = PClass;

XSetWMProperties ( display, window, &windowname,
  &iconname, argv, argc, &size_hints, &wm_hints,
  &class_hint );
}

/* main - основная функция программы */

void main(int argc, char *argv[])
{
 Display * display;  /* Указатель на структуру Display */
 int ScreenNumber;    /* Номер экрана */
 GC gc;				/* Графический контекст */
 XEvent report;
 Window window;

 /* Устанавливаем связь с сервером */
 if ( ( display = XOpenDisplay ( NULL ) ) == NULL ) {
  puts ("Can not connect to the X server!\n");
  exit ( 1 );
 }

 /* Получаем номер основного экрана */
 ScreenNumber = DefaultScreen ( display );

 /* Создаем окно */
 window = XCreateSimpleWindow ( display,
     RootWindow ( display, ScreenNumber ),
     X, Y, WIDTH, HEIGHT, BORDER_WIDTH,
     BlackPixel ( display, ScreenNumber ),
     WhitePixel ( display, ScreenNumber ) );

 /* Задаем рекомендации для менеджера окон */
 SetWindowManagerHints ( display, PRG_CLASS, argv, argc,
   window, X, Y, WIDTH, HEIGHT, WIDTH_MIN,
   HEIGHT_MIN, TITLE, ICON_TITLE, 0 );

 /* Выбираем события,  которые будет обрабатывать программа */
 XSelectInput ( display, window, ExposureMask | KeyPressMask );

 /* Покажем окно */
 XMapWindow ( display, window );

 /* Создадим цикл получения и обработки ошибок */
 while ( 1 ) {
  XNextEvent ( display, &report );

  switch ( report.type ) {
    case Expose :
     /* Запрос на перерисовку */
     if ( report.xexpose.count != 0 )
      break;

     gc = XCreateGC ( display, window, 0 , NULL );

     XSetForeground ( display, gc, BlackPixel ( display, 0) );
     XDrawString ( display, window, gc, 20,50,
       "First example", strlen ( "First example" ) );
     XFreeGC ( display, gc );
     XFlush(display);
     break;

    case KeyPress :
     /* Выход нажатием клавиши клавиатуры */
     XCloseDisplay ( display );
     exit ( 0 );
  }
 }
}

Исходный код программы example1.c

Для сборки программы используется команда:

cc -o primer1.out primer1.c -lX11 -L/usr/X11R6/lib

Здесь cc - имя исполняемого файла компилятора. Как правило, это символическая ссылка на реальное имя компилятора (например, gcc). Параметр -o задает имя исполняемого файла; в нашем случае это hello. -lX11 указывает на необходимость подключения библиотеки Xlib, а -L/usr/X11R6/lib определяет путь к ней.

На рис. 1.3 показан внешний вид приложения после его запуска.

Рис. 1.3. Окно приложения xhello в среде KDE

Программа использует ряд функций, предоставляемых библиотекой Xlib: XOpenDisplay(), XCreateSimpleWindow() и др. Их прототипы, стандартные структуры данных, макросы и константы описаны в следующих основных файлах-модулях: <X11/Xlib.h>, <X11/Xutil.h>, <X11/X.h>, <X11/X11.h>.

Перейдем к рассмотрению самой программы. Она начинается установлением связи с Х-сервером. Делает это функция XOpenDisplay(). Ее аргумент определяет сервер, с которым надо связаться. Если в качестве параметра XOpenDisplay() получает NULL, то она открывает доступ к серверу, который задается переменной среды (environment) DISPLAY. И значение этой переменной, и значение параметра функции имеют следующий формат: host:server.screen, где host - имя компьютера, на котором выполняется сервер, server - номер сервера (обычно это 0), а screen - это номер экрана.

Функция XOpenDisplay() возвращает указатель на структуру типа Display. Это большой набор данных, содержащий информацию о сервере и экранах. Указатель следует запомнить, т.к. он используется в качестве параметра во многих функциях Xlib.

XOpenDisplay() соединяет программу с X сервером, используя протоколы TCP или DECnet, или же с использованием некоторого локального протокола межпроцессного взаимодействия. Если имя машины и номер дисплея разделяются одним знаком двоеточия (:), то XOpenDisplay() производит соединение с использованием протокола TCP. Если же имя машины отделено от номера дисплея двойным двоеточием (::), то для соединения используется протокол DECnet. При отсутствии поля имени машины в имени дисплея, то для соединения используется наиболее быстрые из доступных протоколов. Конкретный X сервер может поддерживать как все, так и некоторые из этих протоколов связи. Конкретные реализации Xlib могут дополнительно поддерживать другие протоколы.

Если соединение проведено удачно, XOpenDisplay() возвращает указатель на структуру Display, которая определяется в <X11/Xlib.h>. Если же установить соединение не удалось, то XOpenDisplay() возвращает NULL. После успешного вызова XOpenDisplay() клиентской программой могут использоваться все экраны дисплея. Номер экрана возвращается функцией XDefaultScreen(). Доступ к полям структур Display и Screen возможен только посредством использования макроопределений и функций.

После того, как связь с сервером установлена, программа "Hello" определяет номер экрана. Для этого используется функция XDefaultScreen(), возвращающий номер основного экрана. Переменная nScreenNum может иметь значение от 0 до величины (ScreenCount(display)-1). Макрос XScreenCount() позволяет получить число экранов, обслуживаемых сервером.

Следующий шаг - создание окна и показ его на дисплее. Для этого программа обращается к фунуции XCreateWindow() или XCreateSimpleWindow(). Для простоты мы используем вторую функцию, параметры которой задают характеристики окна.

  PrWind = XCreateSimpleWindow (
      display, /* указатель на структуру Display */
      RootWindow (display, nScreenNum), /* родительское окно, в данном случае,
			это основное окно программы */
      WND_X, WND_Y,  /* начальные x и y координаты верхнего
					левого угла окна программы */
      WND_WIDTH, WND_HEIGHT,  /* ширина окна и высота окна */
      WND_BORDER_WIDTH, /* ширина края окна */
      BlackPixel ( display, nScreenNum ),  /* цвет переднего плана окна */
      WhitePixel ( display, nScreenNum )   /* цвет фона окна */
     );

Для задания цветов окна используются функции XBlackPixel() и XWhitePixel(). Они возвращают значения пикселей, которые считаются на данном дисплее и экране соответствующими "черному" и "белому" цветам. Функция XCreateSimpleWindow() (XCreateWindow()) возвращает значение типа Window. Это целое число, идентифицирующее созданное окно.

Среди параметров функций, создающих окна, есть те, которые определяют положение окна и его размеры. Эти аргументы принимаются во внимание системой X Window. Исключение составляет случай, когда родительским для создаваемого окна является "корневое" окно экрана. В этом случае решение о положение окна и его размерах принимает менеджер окон. Программа может пытаться повлиять на решение менеджера окон, сообщив ему свои "пожелания" с помощью функции XSetWMProperties().

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

Имя окна и имя пиктограммы должны быть в начале преобразованы в "текстовые свойства", описываемые структурами типа XTextProperty. Это выполняется функцией XStringListToTextProperty().

Для передачи информации о желаемой геометрии окна используется структура XSizeHints.

X Window позволяет сообщить менеджеру также следующее:

После того, как "рекомендации" менеджеру окон переданы, программа выбирает события, на которые она будет реагировать. Для этого вызывается функция XSelectInput(). Ее последний аргумент есть комбинация битовых масок (флагов). В нашем случае это ExposureMask or KeyPressMask. ExposureMask сообщает X Window, что программа обрабатывает событие Expose. Оно посылается сервером каждый раз, когда окно должно быть перерисовано. KeyPressMask выбирает событие KeyPress - нажатие клавиши клавиатуры.

Теперь окно программы создано, но не показано на экране. Чтобы это произошло, надо вызвать функцию XMapWindow(). Заметим, что из-за буферизации событий библиотекой Xlib, окно не будет реально нарисовано, пока программа не обратится к функции получения сообщений от сервера XNextEvent().

Программы для X построены по принципу управляемости событиями. Поэтому, после того, как окно создано, заданы необходимые параметры для менеджера окон, основная ее работа - это получать сообщения от сервера и откликаться на них. Выполняется это в бесконечном цикле. Очередное событие "вынимается" функцией XNextEvent(). Само оно есть переменная типа XEvent, который представляет собой объединение структур. Каждое событие Expose, KeyPress и т.д.) имеет свои данные (и, следовательно, свое поле в объединении XEvent).

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

Когда мы получаем событие Expose, мы должны взять данные события из члена xexpose объединения XEvent. Он содержит различные интересные поля:

Действия по обработке Expose начинаются с создания графического контекста - структуры, которая содержит данные, необходимые для вывода информации, в нашем случае - текста:   gc = XCreateGC (display, window, 0, NULL);

После этого рисуется строка "First example!". Более графический контекст не нужен - он уничтожается:   XFreeGC (display, gc);

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

Приход события KeyPress означает, что программу надо завершить: прекратить связь с сервером:   XCloseDisplay (display); и вызвать функцию exit().

XCloseDisplay() закрывает соединение с Х сервером, закрывает все окна и удаляет идентификаторы ресурсов, созданных клиентом на дисплее. Для удаления только окна без разрыва связи с Х сервером необходимо использовать функции XDestroyWindow() и XDestroySubWindows().

<<< Содержание >>>