Наши партнеры








Книги по Linux (с отзывами читателей)

Библиотека сайта rus-linux.net


Содержание

Книги

Статьи
Программирование в X Window средствами Free Pascal
А. П. Полищук, С. А. Семериков


1.5 Межклиентское взаимодействие


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

Поскольку передача имен - строк произвольной длины - от клиента к серверу может увеличить нагрузку на сеть, X идентифицирует свойства с помощью целых чисел - атомов. Процедура XInternAtom() включает свойство с указанным именем в таблицу сервера и возвращает соответствующий атом. Полный список реализуемых X протоколом атомов можно найти в файле /usr/include/X11/Xatom.h.

Данные свойства рассматриваются сервером как массив единиц длиной 8, 16 или 32 бита. Их конкретная интерпретация осуществляется программами-клиентами.

Каждое свойство имеет тип, который, в свою очередь, также задается тем или иным свойством. Например, свойство, соответствующее атому XA_STRING, задает тип "строка".

Для работы со свойствами кроме XInternAtom() используются следующие процедуры: XChangeProperty() - меняет данные свойства: XGetWindowProperty() - позволяет получить данные свойства.

Особую роль играют свойства, данные которых содержат строки текста. Они так и называются текстовыми и имеют тип "TEXT". Таковыми являются, например, имена (заголовки) окно, имена пиктограмм и т.д. Данные текстового свойства описываются структурой TXTextProperty. Процедура XStringListToTextProperty() переводит список строк в набор данных типа TXTextProperty:
(* Эта переменная будет хранить созданное свойство. *)
var
  window_title_property : TXTextProperty ;
  rc : TStatus;

  (* Строка, преобразуемая в свойство. *)
const
  window_title : PChar = 'hello, world';

(* перевод строки в свойство X. *)
rc := XStringListToTextProperty(@window_title,
                                1,
                                @window_title_property);
(* проверка успешности преобразования. *)
if (rc = 0) then begin
  writeln( 'XStringListToTextProperty - нет памяти');
  halt(1);
end;
XTextPropertyToString() выполняет обратное преобразование.



Менеджер окон - это специальный клиент, в задачи которого входит интерактивное перемещение окон по экрану, изменение их размеров, минимизация (превращение в пиктограмму) и многое другое. Чтобы облегчить менеджеру его нелегкую жизнь, программам рекомендуется при инициализации сообщить о себе определенную информацию. Передается она через предопределенные свойства, которые известны менеджеру и могут быть им прочитаны. Некоторые из свойств (так называемые стандартные) задавать обязательно. Все остальное определяется по усмотрению программы. Наиболее простой способ задать стандартные свойства - обратиться к процедурам XSetStandardProperties() или XSetWMProperties().

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

Имя (заголовок) окна. Идентифицируется атомом XA_WM_NAME и имеет тип "TEXT". Данные свойства - структура TXTextProperty. Для задания свойства используется процедура XStoreName() (XSetWMName()). Получить его можно с помощью XFetchName() (XGetWMName()).

Имя пиктограммы. Идентифицируется атомом XA_WM_ICONNAME и имеет тип "TEXT". Данные свойства - структура TXTextProperty. Для задания свойства используется процедура XSetIconName() (XSetWMIconName()). Получить его можно с помощью XGetIconName() (XGetWMIconName()).

Рекомендации (hints) о геометрии окна. Идентифицируется атомом XA_WM_NORMAL_HINTS и имеет тип XA_WM_SIZE_HINTS. Данные свойства - структура типа TXSizeHints. Для задания свойства используется процедура XSetNormalHints().

В ряде случаев стоит сообщить оконному менеджеру о том, какой размер окна мы хотим получить, и в каких пределах будут изменяться его размеры. Например, для терминальной программы (такой, как xterm), хотелось бы, чтобы окно всегда содержало полное количество строк и столбцов. В других случаях нежелательно давать возможность менять размер окна (например, в диалоговых окнах). Эти пожелания можно передать оконному менеджеру, хотя ничто не помешает ему их проигнорировать. Для этого необходимо создать структуру данных, заполнить ее необходимыми данными и затем использовать функцию XSetWMNormalHints():
(* указатель на структуру рекомендаций о размерах. *)
var
  win_size_hints : PXSizeHints;

win_size_hints := XAllocSizeHints();
if (win_size_hints=nil) then begin
  writeln('XAllocSizeHints - нет памяти');
  halt(1);
end;

(* Инициализация структуры *)
(* Вначале укажем, что передаются пожелания о размерах: *)
(* установим минимальный и начальный размеры. *)
win_size_hints^.flags := PSize OR PMinSize;
(* Затем указываем требуемые границы.              *)
(* в нашем случае - создаем окно минимальным размером 300x200 *)
(* пикселей и устанавливаем начальный размер в 400x250.       *)
win_size_hints^.min_width := 300;
win_size_hints^.min_height := 200;
win_size_hints^.base_width := 400;
win_size_hints^.base_height := 250;

(* Передаем пожелания о размерах оконному менеджеру. *)
XSetWMNormalHints(display, win, win_size_hints);

(* В конце необходимо освободить память из-под структуры. *)
XFree(win_size_hints);
Дополнительные параметры окна: способ работы с клавиатурой, вид и положение пиктограммы. Идентифицируется атомом XA_WM_HINTS и имеет тип XA_WM_HINTS. Данные свойства - структура типа TXWMHints. Для задания свойства используется процедура XSetWMHints(). Структура типа XWMHints, передаваемая функции XSetWMHints(), должна быть подготовлена с помощью XAllocWMHints():
var
  win_hints : PXWMHints;
  icon_pixmap : TPixmap;

const
  icon_bitmap_width=20;
  icon_bitmap_height=20;
  (* Определим битовое изображение в формате Х - *)
  (* оно может быть создано программой xpaint  *)
  icon_bitmap_bits : array [0..59] of byte = (
 $60, $00, $01, $b0, $00, $07, $0c, $03, $00, $04, $04, $00,
 $c2, $18, $00, $03, $30, $00, $01, $60, $00, $f1, $df, $00,
 $c1, $f0, $01, $82, $01, $00, $02, $03, $00, $02, $0c, $00,
 $02, $38, $00, $04, $60, $00, $04, $e0, $00, $04, $38, $00,
 $84, $06, $00, $14, $14, $00, $0c, $34, $00, $00, $00, $00
);


win_hints := XAllocWMHints();
if (win_hints=nil) then begin
  writeln('XAllocWMHints - нет памяти');
  halt(1);
end;

(* установим пожелания о состоянии окна, позиции его иконки *)
(* и ее виде                        *)
win_hints^.flags := StateHint OR IconPositionHint OR IconPixmapHint;

(* Загрузим заданное битовое изображение *)
(* и создадим из него пиксельную карту Х. *)
icon_pixmap := XCreateBitmapFromData(display,
                                     win,
                                     PChar(icon_bitmap_bits),
                                     icon_bitmap_width,
                                     icon_bitmap_height);
if (icon_pixmap=nil) then begin
  writeln('XCreateBitmapFromData: ошибка создания пиксмапа');
  halt(1);
end;


(* Затем детализируем желаемые изменения.             *)
(* в нашем случае - сворачиваем окно, определяем его иконку    *)
(* и устанавливаем позицию иконки в левом верхнем углу экрана.   *)
win_hints^.initial_state := IconicState;
win_hints^.icon_pixmap := icon_pixmap;
win_hints^.icon_x := 0;
win_hints^.icon_y := 0;

(* Передаем пожелания оконному менеджеру. *)
XSetWMHints(display, win, win_hints);

(* В конце необходимо освободить память из-под структуры. *)
XFree(win_hints);
Получить данные свойства можно с помощью XGetWMHints().

Атрибут, характеризующий "временное" окно. Идентифицируется атомом XA_WM_TRANSIENT_FOR и имеет тип XA_STRING. Свойство задается для окон, появляющихся на экране для выполнения вспомогательных функций (диалоги, меню). Такие объекты рассматриваются менеджером по особому. Например, он может не добавлять к окну заголовок и рамку. Данные свойства - идентификатор окна родительского по отношению к данному. Задается свойство с помощью процедуры XSetTransientForHint().

Имена программы и ее класса, идентифицируется атомом XA_WM_CLASS и имеет тип XA_STRING. Данные свойства - структура типа TXClassHints. Задается свойство с помощью процедуры XSetClassHint() и может быть получено с помощью XGetClassHint().

Если окно (окна) программы имеют собственную цветовую палитру, то приложение должно соответствующим образом задать для него атрибут colormap. Программа заносит идентификатор окна (идентификаторы окон) в список, ассоциированный со свойством, имя которого WM_COLORMAP_WINDOWS. Делается это процедурой XSetWMColormapWindows(). Получить список, уже находящийся в свойстве, можно, обратившись к XGetWMColormapWindows().

Когда окно открыто, пользователь посредством менеджера совершает над ним разные действия. Программе может быть желательно перехватывать некоторые из них. Так, например, если окно представляет собой редактор текста, и пользователь пытается его закрыть, то разумно спросить у сидящего за компьютером человека, а не желает ли он предварительно сохранить результаты редакции. Начиная с X11R4 системой предусматривается свойство с именем WM_PROTOCOLS. Оно содержит список атомов, и каждый из них идентифицирует свойство, связанное с действиями, о которых надо оповещать программу. Эти свойства следующие:
WM_TAKE_FOCUS - задается, если программа хочет передавать фокус ввода между своими окнами самостоятельно; в этом случае менеджер не будет управлять фокусом, ввода, а пошлет приложению событие ClientMessage, у которого поле message_type равно атому, соответствующему свойству WM_PROTOCOLS, а поле data.l[0] равно атому, соответствующему свойству WM_TAKE_FOCUS; в ответ на это событие программа должна сама обратиться к XSetInputFocus() для задания окна, имеющего фокус ввода;
WM_SAVE_YOURSELF - задается, если программа хочет перехватить момент своего завершения; менеджер окон посылает приложению событие ClientMessage, у которого поле message_type равно атому, соответствующему свойству WM_PROTOCOLS, а поле data.l[0] равно атому, соответствующему свойству WM_SAVE_YOURSELF; в ответ программа может сохранить свое текущее состояние;
WM_DELETE_WINDOW - задается, если программа хочет перехватить моменты, когда менеджер окон закрывает принадлежащие ей окна; менеджер окон посылает приложению событие ClientMessage, у которого поле message_type равно атому, соответствующему свойству WM_PROTOCOLS, а поле data.l[0] равно атому, соответствующему свойству WM_DELETE_WINDOW; далее программа сама решает, оставить окно на экране или удалить его с помощью XDestroyWindow().
Свойство WM_PROTOCOLS задается процедурой XSetWMProtocols() и может быть получено с помощью XGetWMProtocols().

Приведем фрагмент программы, задающей свойство WM_PROTOCOLS и производящей соответствующую обработку событий.
. . . . . . .
var
  prDisplay : PDisplay;
  nScreenNum : integer;
  prGC : TGC;
  rEvent : TXEvent;
  nWnd : TWindow;
  pnProtocol : array [0..1] of TAtom;
  nWMProtocols : TAtom;

(*
 *Устанавливаем связь с сервером, получаем номер экрана,
 *создаем окно, выбираем события, обрабатываемые программой
 *)
. . . . . . .

(* Задаем свойство WM_PROTOCOLS *)

pnProtocol [0] := XInternAtom (prDisplay, 'WM_TAKE_FOCUS', True);

pnProtocol [1] := XInternAtom (prDisplay, 'WM_SAVE_YOURSELF', True);

nWMProtocols := XInternAtom (prDisplay, 'WM_PROTOCOLS', True);

XSetWMProtocols (prDisplay, nWnd, pnProtocol, 2);

(* Показываем окно *)

XMapWindow (prDisplay, nWnd);

(* Цикл получения и обработки событий *)

while true do
begin
  XNextEvent (prDisplay, @rEvent);

  case (rEvent.type) of
    . . . . . .
    ClientMessage :
    begin
      if (rEvent.xclient.message_type = nWMProtocols) then
      begin
        if (rEvent.xclient.data.l[0] = pnProtocol[0]) then
          writeln('Receiving the input focus.')
        else
          if (rEvent.xclient.data.l[0] = pnProtocol[1]) then
          begin
            XCloseDisplay (prDisplay);
            halt(0);
          end;
      end;
    end;
    . . . . . . .
  end;
end;
. . . . . . .
Заказывается реакция на два события: получение фокуса ввода (WM_TAKE_FOCUS) и завершение программы (WM_SAVE_YOURSELF). Когда сервер посылает событие первого типа, задача печатает соответствующее сообщение на устройства вывода. При приходе события второго типа, программа закрывает связь с сервером и завершается.



1. Составьте программу, которая при получении фокуса ввода перекрашивает свое окно в другой цвет.


2. Составьте программу, порождающую два расположенных рядом дочерних окна, в которых отображаются графики функций sin(x) на отрезке [0; 2π] и exp(x) на отрезке [-2; 2]. Графики масштабировать по размеру окон.


3. Создайте окно, изменяющее свои размеры таким образом, чтобы мышь всегда была в его центре.


4. Создайте окно, "убегающее" от указателя мыши.


5. Создайте программу, которая по нажатию клавиши мыши в основном окне создает новое окно (не более 100 одновременно), а по нажатию клавиши мыши в дочернем окне удаляет его. Если дочернее окно существует более одной минуты, оно должно самоудаляться.


6. Создайте программу моделирования эволюции клеточного автомата "Жизнь", ячейки которого имею два состояния: пусто и заполнено. Если рядом с пустой ячейкой три заполненных, она заполняется. Если рядом с заполненной ячейкой меньше двух или больше трех заполненных, ячейка становится пустой. Размеры модельного поля - 64х64 ячейки, вначале поле пустое. По нажатию любой кнопки мыши состояние ячейки меняется на противоположное, по нажатию пробела осуществляется один шаг эволюции, а по нажатии Escape - выход из программы.