Программирование в X-Window средствами Free Pascal

Перейти к содержанию

1.5.2. Общение с менеджером окон

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

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

В ряде случаев стоит сообщить оконному менеджеру о том, какой размер окна мы хотим получить, и в каких пределах будут изменяться его размеры. Например, для терминальной программы (такой, как 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;
 
(* Загрузим заданное битовое изображение *)
(* и создадим из него пиксельную карту Х. *)
Pixmap 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. Оно содержит список атомов, и каждый из них идентифицирует свойство, связанное с действиями, о которых надо оповещать программу. Эти свойства следующие:

фокус ввода между своими окнами самостоятельно; в этом случае менеджер не будет управлять фокусом, ввода, а пошлет приложению событие 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). Когда сервер посылает событие первого типа, задача печатает соответствующее сообщение на устройства вывода. При приходе события второго типа, программа закрывает связь с сервером и завершается.