Назад

Содержание

Вперед


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

    Продолжая традиции многих изданий, посвященных программированию на С, мы начинаем с программы, рисующей на экране строку "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().